From 3386aca09d2dfe005110e368f4ea47b74131fe98 Mon Sep 17 00:00:00 2001 From: Mingwei Yan Date: Tue, 24 Sep 2024 15:03:15 +0800 Subject: [PATCH 01/21] media: rockchip: isp: update iqtool video 1 diff isp version iqtool copy buffer use the same function 2 iqtool before memcpy sync src_buf cache Signed-off-by: Mingwei Yan Change-Id: I969481c3a59fbff6ec621402e4b19d1647345fde --- drivers/media/platform/rockchip/isp/capture.c | 114 ++++++++++++++++++ drivers/media/platform/rockchip/isp/capture.h | 1 + .../media/platform/rockchip/isp/capture_v21.c | 94 +-------------- .../media/platform/rockchip/isp/capture_v30.c | 93 +------------- .../media/platform/rockchip/isp/capture_v32.c | 102 ++-------------- .../media/platform/rockchip/isp/capture_v39.c | 93 +------------- 6 files changed, 127 insertions(+), 370 deletions(-) diff --git a/drivers/media/platform/rockchip/isp/capture.c b/drivers/media/platform/rockchip/isp/capture.c index 82a798b599c0..6dae02bae982 100644 --- a/drivers/media/platform/rockchip/isp/capture.c +++ b/drivers/media/platform/rockchip/isp/capture.c @@ -365,6 +365,108 @@ void rkisp_config_dmatx_valid_buf(struct rkisp_device *dev) } } +void rkisp_stream_vir_cpy_image(struct work_struct *work) +{ + struct rkisp_vir_cpy *cpy = container_of(work, struct rkisp_vir_cpy, work); + struct rkisp_stream *vir = cpy->stream; + struct rkisp_buffer *src_buf = NULL; + struct vb2_buffer *src_vb = NULL; + struct rkisp_device *isp_dev = vir->ispdev; + const struct vb2_mem_ops *g_ops = isp_dev->hw_dev->mem_ops; + void *src = NULL, *dst = NULL, *mem = NULL; + u32 payload_size = 0; + unsigned long lock_flags = 0; + u32 i; + + v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, + "%s enter\n", __func__); + + vir->streaming = true; + spin_lock_irqsave(&vir->vbq_lock, lock_flags); + if (!list_empty(&cpy->queue)) { + src_buf = list_first_entry(&cpy->queue, + struct rkisp_buffer, queue); + list_del(&src_buf->queue); + } + spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); + + while (src_buf || vir->streaming) { + if (vir->stopping || !vir->streaming) + goto end; + + if (!src_buf) + wait_for_completion(&cpy->cmpl); + + vir->frame_end = false; + + spin_lock_irqsave(&vir->vbq_lock, lock_flags); + if (!src_buf && !list_empty(&cpy->queue)) { + src_buf = list_first_entry(&cpy->queue, struct rkisp_buffer, queue); + list_del(&src_buf->queue); + } + + if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) { + vir->curr_buf = list_first_entry(&vir->buf_queue, + struct rkisp_buffer, queue); + list_del(&vir->curr_buf->queue); + } + spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); + + if (!vir->curr_buf || !src_buf) + goto end; + + src_vb = &src_buf->vb.vb2_buf; + for (i = 0; i < vir->out_isp_fmt.mplanes; i++) { + payload_size = vir->out_fmt.plane_fmt[i].sizeimage; + dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i); + mem = src_vb->planes[i].mem_priv; + src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i); + + if (!src || !dst) + break; + /* sync cache */ + if (mem) + g_ops->finish(mem); + + vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size); + memcpy(dst, src, payload_size); + } + + vir->curr_buf->vb.sequence = src_buf->vb.sequence; + vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp; + vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + vir->curr_buf = NULL; + +end: + if (src_buf) + vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + src_buf = NULL; + + spin_lock_irqsave(&vir->vbq_lock, lock_flags); + + if (!list_empty(&cpy->queue)) { + src_buf = list_first_entry(&cpy->queue, + struct rkisp_buffer, queue); + list_del(&src_buf->queue); + } else if (vir->stopping) { + vir->streaming = false; + } + + spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); + } + + vir->frame_end = true; + + if (vir->stopping) { + vir->stopping = false; + vir->streaming = false; + wake_up(&vir->done); + } + + v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, + "%s exit\n", __func__); +} + /* Get xsubs and ysubs for fourcc formats * * @xsubs: horizontal color samples in a 4*4 matrix, for yuv @@ -746,6 +848,11 @@ static int rkisp_set_fmt(struct rkisp_stream *stream, t = &dev->cap_dev.stream[i]; if (t->out_isp_fmt.fmt_type != FMT_YUV || !t->streaming) continue; + /* isp v32 v33 mp wrap can't use for iqtool */ + if (i == RKISP_STREAM_MP && + (dev->isp_ver == ISP_V32 || dev->isp_ver == ISP_V33) && + dev->cap_dev.wrap_line) + continue; if (t->out_fmt.plane_fmt[0].sizeimage > imagsize) { imagsize = t->out_fmt.plane_fmt[0].sizeimage; *pixm = t->out_fmt; @@ -1252,6 +1359,13 @@ static int rkisp_set_iqtool_connect_id(struct rkisp_stream *stream, int stream_i goto err; } + if ((dev->isp_ver == ISP_V32 || dev->isp_ver == ISP_V33) && + (stream_id == RKISP_STREAM_MP) && + dev->cap_dev.wrap_line) { + v4l2_err(&dev->v4l2_dev, "isp v32 v33 mp wrap can't use for iqtool"); + goto err; + } + stream->conn_id = stream_id; return 0; err: diff --git a/drivers/media/platform/rockchip/isp/capture.h b/drivers/media/platform/rockchip/isp/capture.h index 4c3d3fc627e9..65b7256fe65e 100644 --- a/drivers/media/platform/rockchip/isp/capture.h +++ b/drivers/media/platform/rockchip/isp/capture.h @@ -345,6 +345,7 @@ extern struct stream_config rkisp_mp_stream_config; extern struct stream_config rkisp_sp_stream_config; extern struct rockit_isp_ops rockit_isp_ops; +void rkisp_stream_vir_cpy_image(struct work_struct *work); void rkisp_stream_buf_done_early(struct rkisp_device *dev); void rkisp_stream_buf_done(struct rkisp_stream *stream, struct rkisp_buffer *buf); diff --git a/drivers/media/platform/rockchip/isp/capture_v21.c b/drivers/media/platform/rockchip/isp/capture_v21.c index e354747e9530..cb394befd7ab 100644 --- a/drivers/media/platform/rockchip/isp/capture_v21.c +++ b/drivers/media/platform/rockchip/isp/capture_v21.c @@ -1404,98 +1404,6 @@ static void rkisp_stream_stop(struct rkisp_stream *stream) stream->interlaced = false; } -static void vir_cpy_image(struct work_struct *work) -{ - struct rkisp_vir_cpy *cpy = - container_of(work, struct rkisp_vir_cpy, work); - struct rkisp_stream *vir = cpy->stream; - struct rkisp_buffer *src_buf = NULL; - unsigned long lock_flags = 0; - u32 i; - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s enter\n", __func__); - - vir->streaming = true; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - while (src_buf || vir->streaming) { - if (vir->stopping || !vir->streaming) - goto end; - - if (!src_buf) - wait_for_completion(&cpy->cmpl); - - vir->frame_end = false; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!src_buf && !list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - - if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) { - vir->curr_buf = list_first_entry(&vir->buf_queue, - struct rkisp_buffer, queue); - list_del(&vir->curr_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - if (!vir->curr_buf || !src_buf) - goto end; - - for (i = 0; i < vir->out_isp_fmt.mplanes; i++) { - u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage; - void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i); - void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i); - - if (!src || !dst) - break; - vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size); - memcpy(dst, src, payload_size); - } - - vir->curr_buf->vb.sequence = src_buf->vb.sequence; - vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp; - vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - vir->curr_buf = NULL; -end: - if (src_buf) - vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - src_buf = NULL; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } else if (vir->stopping) { - vir->streaming = false; - } - - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - } - - vir->frame_end = true; - - if (vir->stopping) { - vir->stopping = false; - vir->streaming = false; - wake_up(&vir->done); - } - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s exit\n", __func__); -} - - /* * Most of registers inside rockchip isp1 have shadow register since * they must be not changed during processing a frame. @@ -1793,7 +1701,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count) struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id]; if (t->streaming) { - INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image); + INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image); init_completion(&dev->cap_dev.vir_cpy.cmpl); INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue); dev->cap_dev.vir_cpy.stream = stream; diff --git a/drivers/media/platform/rockchip/isp/capture_v30.c b/drivers/media/platform/rockchip/isp/capture_v30.c index a4acb0eeafe9..532b1de9e8b8 100644 --- a/drivers/media/platform/rockchip/isp/capture_v30.c +++ b/drivers/media/platform/rockchip/isp/capture_v30.c @@ -1305,97 +1305,6 @@ end: mutex_unlock(&dev->hw_dev->dev_lock); } -static void vir_cpy_image(struct work_struct *work) -{ - struct rkisp_vir_cpy *cpy = - container_of(work, struct rkisp_vir_cpy, work); - struct rkisp_stream *vir = cpy->stream; - struct rkisp_buffer *src_buf = NULL; - unsigned long lock_flags = 0; - u32 i; - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s enter\n", __func__); - - vir->streaming = true; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - while (src_buf || vir->streaming) { - if (vir->stopping || !vir->streaming) - goto end; - - if (!src_buf) - wait_for_completion(&cpy->cmpl); - - vir->frame_end = false; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!src_buf && !list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - - if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) { - vir->curr_buf = list_first_entry(&vir->buf_queue, - struct rkisp_buffer, queue); - list_del(&vir->curr_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - if (!vir->curr_buf || !src_buf) - goto end; - - for (i = 0; i < vir->out_isp_fmt.mplanes; i++) { - u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage; - void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i); - void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i); - - if (!src || !dst) - break; - vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size); - memcpy(dst, src, payload_size); - } - - vir->curr_buf->vb.sequence = src_buf->vb.sequence; - vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp; - vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - vir->curr_buf = NULL; -end: - if (src_buf) - vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - src_buf = NULL; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } else if (vir->stopping) { - vir->streaming = false; - } - - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - } - - vir->frame_end = true; - - if (vir->stopping) { - vir->stopping = false; - vir->streaming = false; - wake_up(&vir->done); - } - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s exit\n", __func__); -} - static int rkisp_stream_start(struct rkisp_stream *stream) { struct v4l2_device *v4l2_dev = &stream->ispdev->v4l2_dev; @@ -1452,7 +1361,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count) struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id]; if (t->streaming) { - INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image); + INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image); init_completion(&dev->cap_dev.vir_cpy.cmpl); INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue); dev->cap_dev.vir_cpy.stream = stream; diff --git a/drivers/media/platform/rockchip/isp/capture_v32.c b/drivers/media/platform/rockchip/isp/capture_v32.c index ca0186d52d53..db0b1b33e52b 100644 --- a/drivers/media/platform/rockchip/isp/capture_v32.c +++ b/drivers/media/platform/rockchip/isp/capture_v32.c @@ -1943,97 +1943,6 @@ end: } } -static void vir_cpy_image(struct work_struct *work) -{ - struct rkisp_vir_cpy *cpy = - container_of(work, struct rkisp_vir_cpy, work); - struct rkisp_stream *vir = cpy->stream; - struct rkisp_buffer *src_buf = NULL; - unsigned long lock_flags = 0; - u32 i; - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s enter\n", __func__); - - vir->streaming = true; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - while (src_buf || vir->streaming) { - if (vir->stopping || !vir->streaming) - goto end; - - if (!src_buf) - wait_for_completion(&cpy->cmpl); - - vir->frame_end = false; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!src_buf && !list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - - if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) { - vir->curr_buf = list_first_entry(&vir->buf_queue, - struct rkisp_buffer, queue); - list_del(&vir->curr_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - if (!vir->curr_buf || !src_buf) - goto end; - - for (i = 0; i < vir->out_isp_fmt.mplanes; i++) { - u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage; - void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i); - void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i); - - if (!src || !dst) - break; - vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size); - memcpy(dst, src, payload_size); - } - - vir->curr_buf->vb.sequence = src_buf->vb.sequence; - vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp; - vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - vir->curr_buf = NULL; -end: - if (src_buf) - vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - src_buf = NULL; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } else if (vir->stopping) { - vir->streaming = false; - } - - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - } - - vir->frame_end = true; - - if (vir->stopping) { - vir->stopping = false; - vir->streaming = false; - wake_up(&vir->done); - } - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s exit\n", __func__); -} - static int rkisp_stream_start(struct rkisp_stream *stream) { struct rkisp_device *dev = stream->ispdev; @@ -2096,7 +2005,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count) struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id]; if (t->streaming) { - INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image); + INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image); init_completion(&dev->cap_dev.vir_cpy.cmpl); INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue); dev->cap_dev.vir_cpy.stream = stream; @@ -2342,11 +2251,14 @@ int rkisp_register_stream_v32(struct rkisp_device *dev) ret = rkisp_stream_init(dev, RKISP_STREAM_SP); if (ret < 0) goto err_free_mp; + ret = rkisp_stream_init(dev, RKISP_STREAM_VIR); + if (ret < 0) + goto err_free_sp; if (dev->isp_ver == ISP_V32) { ret = rkisp_stream_init(dev, RKISP_STREAM_BP); if (ret < 0) - goto err_free_sp; + goto err_free_vir; ret = rkisp_stream_init(dev, RKISP_STREAM_MPDS); if (ret < 0) goto err_free_bp; @@ -2370,6 +2282,8 @@ err_free_mpds: rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_MPDS]); err_free_bp: rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_BP]); +err_free_vir: + rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_VIR]); err_free_sp: rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_SP]); err_free_mp: @@ -2387,6 +2301,8 @@ void rkisp_unregister_stream_v32(struct rkisp_device *dev) rkisp_unregister_stream_vdev(stream); stream = &cap_dev->stream[RKISP_STREAM_SP]; rkisp_unregister_stream_vdev(stream); + stream = &cap_dev->stream[RKISP_STREAM_VIR]; + rkisp_unregister_stream_vdev(stream); if (dev->isp_ver == ISP_V32) { stream = &cap_dev->stream[RKISP_STREAM_BP]; rkisp_unregister_stream_vdev(stream); diff --git a/drivers/media/platform/rockchip/isp/capture_v39.c b/drivers/media/platform/rockchip/isp/capture_v39.c index 1ed0b0a11ac7..e276c77248da 100644 --- a/drivers/media/platform/rockchip/isp/capture_v39.c +++ b/drivers/media/platform/rockchip/isp/capture_v39.c @@ -1387,97 +1387,6 @@ end: mutex_unlock(&dev->hw_dev->dev_lock); } -static void vir_cpy_image(struct work_struct *work) -{ - struct rkisp_vir_cpy *cpy = - container_of(work, struct rkisp_vir_cpy, work); - struct rkisp_stream *vir = cpy->stream; - struct rkisp_buffer *src_buf = NULL; - unsigned long lock_flags = 0; - u32 i; - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s enter\n", __func__); - - vir->streaming = true; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - while (src_buf || vir->streaming) { - if (vir->stopping || !vir->streaming) - goto end; - - if (!src_buf) - wait_for_completion(&cpy->cmpl); - - vir->frame_end = false; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!src_buf && !list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } - - if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) { - vir->curr_buf = list_first_entry(&vir->buf_queue, - struct rkisp_buffer, queue); - list_del(&vir->curr_buf->queue); - } - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - - if (!vir->curr_buf || !src_buf) - goto end; - - for (i = 0; i < vir->out_isp_fmt.mplanes; i++) { - u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage; - void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i); - void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i); - - if (!src || !dst) - break; - vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size); - memcpy(dst, src, payload_size); - } - - vir->curr_buf->vb.sequence = src_buf->vb.sequence; - vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp; - vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - vir->curr_buf = NULL; -end: - if (src_buf) - vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - src_buf = NULL; - spin_lock_irqsave(&vir->vbq_lock, lock_flags); - - if (!list_empty(&cpy->queue)) { - src_buf = list_first_entry(&cpy->queue, - struct rkisp_buffer, queue); - list_del(&src_buf->queue); - } else if (vir->stopping) { - vir->streaming = false; - } - - spin_unlock_irqrestore(&vir->vbq_lock, lock_flags); - } - - vir->frame_end = true; - - if (vir->stopping) { - vir->stopping = false; - vir->streaming = false; - wake_up(&vir->done); - } - - v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev, - "%s exit\n", __func__); -} - static int rkisp_stream_start(struct rkisp_stream *stream) { struct rkisp_device *dev = stream->ispdev; @@ -1536,7 +1445,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count) struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id]; if (t->streaming) { - INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image); + INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image); init_completion(&dev->cap_dev.vir_cpy.cmpl); INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue); dev->cap_dev.vir_cpy.stream = stream; From 97d61e62ad029814cdf79ec0572fe43212003886 Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Wed, 11 Sep 2024 11:36:30 +0800 Subject: [PATCH 02/21] ARM: dts: rockchip: rk3506: Add trim configure for tsadc Signed-off-by: Ye Zhang Change-Id: I0c77178696780d160ed52034d80c2e0e2bb1772c --- arch/arm/boot/dts/rk3506.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/rk3506.dtsi b/arch/arm/boot/dts/rk3506.dtsi index 008580d38ccf..91edbd78f0e0 100644 --- a/arch/arm/boot/dts/rk3506.dtsi +++ b/arch/arm/boot/dts/rk3506.dtsi @@ -1244,6 +1244,13 @@ log_leakage: log-leakage@1f { reg = <0x1f 0x1>; }; + cpu_tsadc_trim_l: cpu-tsadc-trim-l@20 { + reg = <0x20 0x1>; + }; + cpu_tsadc_trim_h: cpu-tsadc-trim-h@21 { + reg = <0x21 0x1>; + bits = <0 2>; + }; }; audio_codec: audio-codec@ff4f8000 { @@ -1352,6 +1359,8 @@ rockchip,hw-tshut-temp = <120000>; rockchip,hw-tshut-mode = <0>; /* tshut mode 0:CRU 1:GPIO */ rockchip,hw-tshut-polarity = <0>; /* tshut polarity 0:LOW 1:HIGH */ + nvmem-cells = <&cpu_tsadc_trim_l>, <&cpu_tsadc_trim_h>; + nvmem-cell-names = "trim_l", "trim_h"; status = "disabled"; }; From a7ec1b0ba108f9ab62ac02b7af0475e3ea3e8f1c Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Wed, 11 Sep 2024 11:39:11 +0800 Subject: [PATCH 03/21] thermal: rockchip: Add trim temperature for rk3506 Signed-off-by: Ye Zhang Change-Id: I8fd6ae6931c01d285e340670807b68564eb6ffcf --- drivers/thermal/rockchip_thermal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 432d0dc31c2f..e5e6bf4e9c8b 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -1911,6 +1911,8 @@ static const struct rockchip_tsadc_chip rk3506_tsadc_data = { .set_alarm_temp = rk_tsadcv3_alarm_temp, .set_tshut_temp = rk_tsadcv3_tshut_temp, .set_tshut_mode = rk_tsadcv4_tshut_mode, + .get_trim_code = rk_tsadcv2_get_trim_code, + .trim_slope = 594, .table = { .id = rk3506_code_table, .length = ARRAY_SIZE(rk3506_code_table), From 8aa60946d700b27735850d5c77e294050269629e Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 25 Sep 2024 11:16:03 +0800 Subject: [PATCH 04/21] ARM: dts: rockchip: rk3506g-evb1: update cma size for bt1120/bt656/mcu/rgb display boards Sync the cma size with mipi display board. It is needed to allocate 22MB cma to meet the rockit application requirements for rk3506. Change-Id: I94005a69e17f9d0d6aaedee271ba507cc9596ea8 Signed-off-by: Damon Ding --- arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts | 4 ++++ arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts | 4 ++++ arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts | 4 ++++ arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts index 0395310dc5b5..a2812db1820a 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts @@ -14,6 +14,10 @@ compatible = "rockchip,rk3506g-evb1-v10-mcu-k350c4516t", "rockchip,rk3506"; }; +&cma { + size = <0x1600000>; +}; + &rgb { status = "okay"; rockchip,data-sync-bypass; diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts index 0e894b1fe598..cc845d6ed39f 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts @@ -52,6 +52,10 @@ }; }; +&cma { + size = <0x1600000>; +}; + &rgb { status = "okay"; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts index 5ac3b9d4e40a..4a30d24c21c9 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts @@ -14,6 +14,10 @@ compatible = "rockchip,rk3506g-evb1-v10-sii9022-bt1120-to-hdmi", "rockchip,rk3506"; }; +&cma { + size = <0x1600000>; +}; + &i2c2 { clock-frequency = <400000>; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts index 25d2f2ef2e1d..f78f90ea7c89 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts @@ -14,6 +14,10 @@ compatible = "rockchip,rk3506g-evb1-v10-sii9022-rgb2hdmi", "rockchip,rk3506"; }; +&cma { + size = <0x1600000>; +}; + &i2c2 { clock-frequency = <400000>; pinctrl-0 = <&rm_io4_i2c2_scl &rm_io5_i2c2_sda>; From c15a51132c04bc680fe28005f1cb60c252e0b3f1 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Fri, 20 Sep 2024 14:52:04 +0800 Subject: [PATCH 05/21] spi: rockchip: The cs-high function of the controller is only valid for function io Change-Id: Ic4af757b625d3a2278f79664097c6394fcb2d7a2 Signed-off-by: Jon Lin --- drivers/spi/spi-rockchip.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index faa1757d920c..f9800d28ddcd 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -295,12 +295,12 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) /* Keep things powered as long as CS is asserted */ pm_runtime_get_sync(rs->dev); - if (spi->cs_gpiod) + if (spi_get_csgpiod(spi, 0)) ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1); else ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select)); } else { - if (spi->cs_gpiod) + if (spi_get_csgpiod(spi, 0)) ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1); else ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select)); @@ -642,7 +642,7 @@ static int rockchip_spi_config(struct rockchip_spi *rs, cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; if (spi->mode & SPI_LSB_FIRST) cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET; - if (spi->mode & SPI_CS_HIGH) + if (spi->mode & SPI_CS_HIGH && !spi_get_csgpiod(spi, 0)) cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET; if (xfer->rx_buf && xfer->tx_buf) { @@ -903,20 +903,15 @@ static int rockchip_spi_setup(struct spi_device *spi) struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller); u32 cr0; - if (!spi->cs_gpiod && (spi->mode & SPI_CS_HIGH) && !rs->cs_high_supported) { - dev_warn(&spi->dev, "setup: non GPIO CS can't be active-high\n"); - return -EINVAL; - } - pm_runtime_get_sync(rs->dev); cr0 = readl_relaxed(rs->regs + ROCKCHIP_SPI_CTRLR0); cr0 &= ~(0x3 << CR0_SCPH_OFFSET); cr0 |= ((spi->mode & 0x3) << CR0_SCPH_OFFSET); - if (spi->mode & SPI_CS_HIGH && spi->chip_select <= 1) + if (spi->mode & SPI_CS_HIGH && spi->chip_select <= 1 && !spi_get_csgpiod(spi, 0)) cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET; - else if (spi->chip_select <= 1) + else if (spi->chip_select <= 1 && !spi_get_csgpiod(spi, 0)) cr0 &= ~(BIT(spi->chip_select) << CR0_SOI_OFFSET); if (spi_controller_is_slave(spi->controller)) cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET; From d2f3a5b3eeba9d6f73b38be9891b9e5538e40258 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Fri, 13 Sep 2024 14:50:19 +0800 Subject: [PATCH 06/21] PCI: rockchip: dw: Optimize the pm process L1SS workflow Change-Id: I81166253e515ffac1ac1c6de44f40c8f11e04758 Signed-off-by: Jon Lin --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 8b9b13e812a2..8d6720bd07db 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -143,6 +143,7 @@ struct rk_pcie { raw_spinlock_t intx_lock; u16 aspm; u32 l1ss_ctl1; + u32 l1ss_ctl2; struct dentry *debugfs; u32 msi_vector_num; struct workqueue_struct *hot_rst_wq; @@ -1434,46 +1435,69 @@ static void rk_pcie_downstream_dev_to_d0(struct rk_pcie *rk_pcie, bool enable) } /* Save and restore root bus ASPM */ - if (enable) { - if (rk_pcie->l1ss_ctl1) - dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1, rk_pcie->l1ss_ctl1); - - /* rk_pcie->aspm woule be saved in advance when enable is false */ - dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, rk_pcie->aspm); - } else { + if (!enable) { val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1); - if (val & PCI_L1SS_CTL1_L1SS_MASK) + if (val & PCI_L1SS_CTL1_L1SS_MASK) { rk_pcie->l1ss_ctl1 = val; - else + rk_pcie->l1ss_ctl2 = dw_pcie_readl_dbi(rk_pcie->pci, + bridge->l1ss + PCI_L1SS_CTL2); + } else { rk_pcie->l1ss_ctl1 = 0; + rk_pcie->l1ss_ctl2 = 0; + } val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL); rk_pcie->aspm = val & PCI_EXP_LNKCTL_ASPMC; - val &= ~(PCI_EXP_LNKCAP_ASPM_L1 | PCI_EXP_LNKCAP_ASPM_L0S); - dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, val); } + if (enable) { + if (rk_pcie->l1ss_ctl1) { + dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL2, + rk_pcie->l1ss_ctl2); + dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1, + rk_pcie->l1ss_ctl1); + if (bridge->ltr_path) + pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2, 0x0400); + } + } + + if (enable) { + list_for_each_entry(pdev, &root_bus->devices, bus_list) { + if (PCI_SLOT(pdev->devfn) == 0) { + if (rk_pcie->l1ss_ctl1) { + pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL2, + rk_pcie->l1ss_ctl2); + pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, + rk_pcie->l1ss_ctl1); + } + } + } + } + + if (enable) + dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, rk_pcie->aspm); + list_for_each_entry(pdev, &root_bus->devices, bus_list) { if (PCI_SLOT(pdev->devfn) == 0) { if (pci_set_power_state(pdev, PCI_D0)) dev_err(rk_pcie->pci->dev, - "Failed to transition %s to D3hot state\n", + "Failed to transition %s to D0 state\n", dev_name(&pdev->dev)); - if (enable) { - if (rk_pcie->l1ss_ctl1) { - pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, &val); - val &= ~PCI_L1SS_CTL1_L1SS_MASK; - val |= (rk_pcie->l1ss_ctl1 & PCI_L1SS_CTL1_L1SS_MASK); - pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, val); - } - + if (enable) pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC, rk_pcie->aspm); - } else { - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); - } + PCI_EXP_LNKCTL_ASPMC, + rk_pcie->aspm); + else + pci_disable_link_state(pdev, + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); } } + + if (!enable) { + val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL); + val &= ~(PCI_EXP_LNKCTL_ASPM_L1 | PCI_EXP_LNKCTL_ASPM_L0S); + dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, val); + } } #endif From 9bd53f1e79d4bc881f6c8c20178c70911b140b44 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Sat, 14 Sep 2024 10:53:11 +0800 Subject: [PATCH 07/21] net: wireless: rockchip_wlan: bcmdhd: Calling ROCKCHIP_PCIE_PM_CTRL_RESET when wifi on Change-Id: I07fef33044087354e2ed667121307e54fadbee63 Signed-off-by: Jon Lin --- .../rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c | 15 +++++++++++++++ .../rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c | 5 ----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c index ff457ea17130..ab94a95e615d 100755 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c @@ -108,6 +108,10 @@ #endif /* DHD_CONTROL_PCIE_CPUCORE_WIFI_TURNON */ #include +#ifdef CONFIG_ARCH_ROCKCHIP +#include +#endif + #define EXTENDED_PCIE_DEBUG_DUMP 1 /* Enable Extended pcie registers dump */ #define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ @@ -7920,6 +7924,17 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) DHD_ERROR(("%s: == Power ON ==\n", __FUNCTION__)); /* PCIe RC Turn on */ do { +#ifdef CONFIG_ARCH_ROCKCHIP + if (bus->rc_dev) { + bcmerror = rockchip_dw_pcie_pm_ctrl_for_user(bus->rc_dev, ROCKCHIP_PCIE_PM_CTRL_RESET); + if (bcmerror) { + DHD_ERROR(("%s Failed to bring up PCIe link\n", __FUNCTION__)); + OSL_SLEEP(10); + continue; + } + + } +#endif /* CONFIG_ARCH_ROCKCHIP */ bcmerror = dhdpcie_bus_start_host_dev(bus); if (!bcmerror) { DHD_ERROR_MEM(("%s: dhdpcie_bus_start_host_dev OK\n", diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c index 832bf23844ac..0daa1e6e38f8 100755 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c @@ -2403,11 +2403,6 @@ dhdpcie_start_host_dev(dhd_bus_t *bus) ret = msm_pcie_pm_control(MSM_PCIE_RESUME, bus->dev->bus->number, bus->dev, NULL, 0); #endif /* CONFIG_ARCH_MSM */ -#ifdef CONFIG_ARCH_ROCKCHIP - if (bus->rc_dev) - ret = rockchip_dw_pcie_pm_ctrl_for_user(bus->rc_dev, ROCKCHIP_PCIE_PM_CTRL_RESET); -#endif /* CONFIG_ARCH_ROCKCHIP */ - if (ret) { DHD_ERROR(("%s Failed to bring up PCIe link\n", __FUNCTION__)); goto done; From 5aa0cd714373431791c733904b08e94e125ba472 Mon Sep 17 00:00:00 2001 From: Jianwei Zheng Date: Wed, 25 Sep 2024 11:35:08 +0800 Subject: [PATCH 08/21] ARM: dts: rockchip: rk3506g-iotest: add vbus-always-on for u2phy The default hardware design of rk3506g-iotest does not support USB VBUS detect, so add vbus-always-on property for u2phy. Change-Id: I9a1a130333a1843335cf2e28c6b705197f086456 Signed-off-by: Jianwei Zheng --- arch/arm/boot/dts/rk3506g-iotest-v10.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/rk3506g-iotest-v10.dts b/arch/arm/boot/dts/rk3506g-iotest-v10.dts index cb9f04dee728..73173d3a7714 100644 --- a/arch/arm/boot/dts/rk3506g-iotest-v10.dts +++ b/arch/arm/boot/dts/rk3506g-iotest-v10.dts @@ -203,6 +203,7 @@ }; &u2phy_otg0 { + rockchip,vbus-always-on; status = "okay"; }; From 2f7fe7a5cc82debae55edc88c5f4bda0f22d986c Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 25 Sep 2024 11:33:17 +0800 Subject: [PATCH 09/21] ARM: dts: rockchip: rk3506g-evb1: enable rockchip,vbus-always-on for bt1120/bt656/mcu/rgb display board The GPIO1_C5 is multiplexed by VO_LCDC_D6, which needed by bt1120/bt656/mcu/rgb, and USB20_OTG0_VBUSDET. Enabling rockchip,vbus-always-on can make ADB work well without the vbus detection pin. Change-Id: I7fa705436cf8a1e41f0f61f4941c24f3d9f433b0 Signed-off-by: Damon Ding --- arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts | 4 ++++ arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts | 4 ++++ arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts | 4 ++++ arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts index a2812db1820a..13b444b640b5 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-mcu-k350c4516t.dts @@ -218,6 +218,10 @@ status = "okay"; }; +&u2phy_otg0 { + rockchip,vbus-always-on; +}; + &vop { mcu-timing { mcu-pix-total = <6>; diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts index cc845d6ed39f..8a98e6a8ef5f 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-rgb-Q7050ITH2641AA1T.dts @@ -82,3 +82,7 @@ &route_rgb { status = "okay"; }; + +&u2phy_otg0 { + rockchip,vbus-always-on; +}; diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts index 4a30d24c21c9..d0a2ac0a27e6 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-bt1120-to-hdmi.dts @@ -93,3 +93,7 @@ &route_rgb { status = "okay"; }; + +&u2phy_otg0 { + rockchip,vbus-always-on; +}; diff --git a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts index f78f90ea7c89..b2969c1987e5 100644 --- a/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts +++ b/arch/arm/boot/dts/rk3506g-evb1-v10-sii9022-rgb2hdmi.dts @@ -84,3 +84,7 @@ &route_rgb { status = "okay"; }; + +&u2phy_otg0 { + rockchip,vbus-always-on; +}; From 8ad666171fae34c47e180db1a82cf2fed442d012 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 25 Sep 2024 09:33:25 +0800 Subject: [PATCH 10/21] arm64: dts: rockchip: rk3576-test5-v10 modify pwdn-gpio of camera Signed-off-by: Zefa Chen Change-Id: I117b9608f2247a8401f0bf978019e81e4ad906d0 --- .../arm64/boot/dts/rockchip/rk3576-test5-v10.dts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-test5-v10.dts b/arch/arm64/boot/dts/rockchip/rk3576-test5-v10.dts index b2eb1d337de5..2ee81e5b4878 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-test5-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-test5-v10.dts @@ -15,3 +15,19 @@ model = "Rockchip RK3576 TEST5 V10 Board"; compatible = "rockchip,rk3576-test5-v10", "rockchip,rk3576"; }; + +&imx464_0 { + pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>; +}; + +&imx464_1 { + pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>; +}; + +&os04a10 { + pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>; +}; + +&sc4336 { + pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>; +}; From b28ccdbd6ddf9b83c3f11f2ee53ced1db17b731c Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Thu, 5 Sep 2024 12:29:31 +0800 Subject: [PATCH 11/21] PCI: dw: rockchip: Fully reset controller in initial process Change-Id: Iee871db366695539a92f86da7ea5971780bf52fe Signed-off-by: Jon Lin Signed-off-by: Shawn Lin --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 8d6720bd07db..1729a0bce2dc 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -1313,6 +1313,8 @@ static int rk_pcie_really_probe(void *p) if (ret) goto release_driver; + reset_control_assert(rk_pcie->rsts); + udelay(10); reset_control_deassert(rk_pcie->rsts); ret = clk_bulk_prepare_enable(rk_pcie->clk_cnt, rk_pcie->clks); From 4ca55c87f91c75f2c048046cdd40ed56e210b69e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 3 Sep 2024 11:26:09 +0800 Subject: [PATCH 12/21] PCI: rockchip: dw: Add retrain link support Speed change is set via dw_pcie_setup_rc(), so if both of links support gen2 or gen3, auto speed change will happen. However, if it's not, provide a manual speed change for EP function driver. Signed-off-by: Shawn Lin Change-Id: Ib0dc765452aef0723968c5d48b5b44de24ca141e --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 31 +++++++++++++++++++ include/linux/aspm_ext.h | 1 + 2 files changed, 32 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 1729a0bce2dc..d8f4a3c2c811 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -304,6 +304,34 @@ static int rk_pcie_disable_power(struct rk_pcie *rk_pcie) return ret; } +static void rk_pcie_retrain(struct dw_pcie *pci) +{ + u32 pcie_cap_off; + u16 lnk_stat, lnk_ctl; + int ret; + + /* + * Set retrain bit if current speed is 2.5 GB/s, + * but the PCIe root port support is > 2.5 GB/s. + */ + if (pci->link_gen < 2) + return; + + dev_info(pci->dev, "Retrain link..\n"); + pcie_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + lnk_stat = dw_pcie_readw_dbi(pci, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = dw_pcie_readw_dbi(pci, pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + dw_pcie_writew_dbi(pci, pcie_cap_off + PCI_EXP_LNKCTL, lnk_ctl); + + ret = readw_poll_timeout(pci->dbi_base + pcie_cap_off + PCI_EXP_LNKSTA, + lnk_stat, ((lnk_stat & PCI_EXP_LNKSTA_LT) == 0), 1000, HZ); + if (ret) + dev_err(pci->dev, "Retrain link timeout\n"); + } +} + static int rk_pcie_establish_link(struct dw_pcie *pci) { int retries, power; @@ -1684,6 +1712,9 @@ int rockchip_dw_pcie_pm_ctrl_for_user(struct pci_dev *dev, enum rockchip_pcie_pm rockchip_dw_pcie_suspend(rk_pcie->pci->dev); rockchip_dw_pcie_resume(rk_pcie->pci->dev); break; + case ROCKCHIP_PCIE_PM_RETRAIN_LINK: + rk_pcie_retrain(pci); + break; default: dev_err(rk_pcie->pci->dev, "%s flag=%d invalid\n", __func__, flag); return -EINVAL; diff --git a/include/linux/aspm_ext.h b/include/linux/aspm_ext.h index c4580dfc80f7..140de4389796 100644 --- a/include/linux/aspm_ext.h +++ b/include/linux/aspm_ext.h @@ -17,6 +17,7 @@ static inline bool pcie_aspm_ext_is_in_l1sub_state(struct pci_dev *pdev) { retur enum rockchip_pcie_pm_ctrl_flag { ROCKCHIP_PCIE_PM_CTRL_RESET = 1, + ROCKCHIP_PCIE_PM_RETRAIN_LINK = 2, }; int rockchip_dw_pcie_pm_ctrl_for_user(struct pci_dev *dev, enum rockchip_pcie_pm_ctrl_flag flag); From 3f258a6c3c5ba617e345994cddb983cb5e5d5224 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Thu, 5 Sep 2024 12:35:45 +0800 Subject: [PATCH 13/21] phy: rockchip-snps-pcie3: Check the mplla_state/mpllb_state Change-Id: I5463cace81bb79b6024bc0ac9d0d8de5bfb9ebdb Signed-off-by: Jon Lin Signed-off-by: Shawn Lin --- drivers/phy/rockchip/phy-rockchip-snps-pcie3.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index 7eb8899e9b66..90ce4f0f327d 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -32,7 +32,8 @@ #define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 #define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 #define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 -#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0)) +#define RK3588_SRAM_INIT_DONE(reg) ((reg & 0xff) == 0x49) +#define RK3588_SRAM_INIT_TIMEOUT 20000 struct rockchip_p3phy_ops; @@ -148,11 +149,14 @@ static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv) ret = regmap_read_poll_timeout(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_STATUS1, reg, RK3588_SRAM_INIT_DONE(reg), - 0, 500); - ret |= regmap_read_poll_timeout(priv->phy_grf, - RK3588_PCIE3PHY_GRF_PHY1_STATUS1, - reg, RK3588_SRAM_INIT_DONE(reg), - 0, 500); + 100, RK3588_SRAM_INIT_TIMEOUT); + if (priv->pcie30_phymode == PHY_MODE_PCIE_AGGREGATION) { + ret |= regmap_read_poll_timeout(priv->phy_grf, + RK3588_PCIE3PHY_GRF_PHY1_STATUS1, + reg, RK3588_SRAM_INIT_DONE(reg), + 100, RK3588_SRAM_INIT_TIMEOUT); + } + if (ret) dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n", __func__, reg); From 21b66851f25020812071b57b3666f6001a5c8923 Mon Sep 17 00:00:00 2001 From: Luo Wei Date: Tue, 24 Sep 2024 15:29:54 +0800 Subject: [PATCH 14/21] arm64: dts: rockchip: rk3576-vehicle-evb: fix evb v20 pwm setting Change-Id: Ibcfbf87806151b479cc59a979ea8049b30f11258 Signed-off-by: Luo Wei --- .../rk3576-vehicle-evb-v20-serdes-mfd-display-maxim.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-v20-serdes-mfd-display-maxim.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-v20-serdes-mfd-display-maxim.dtsi index 7e3fe48e4877..88dc57a5b8cc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-v20-serdes-mfd-display-maxim.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-vehicle-evb-v20-serdes-mfd-display-maxim.dtsi @@ -1640,7 +1640,7 @@ /* dsi->serdes->lvds_panel */ &pwm1_6ch_1 { status = "okay"; - pinctrl-0 = <&pwm1m0_ch1>; + pinctrl-0 = <&pwm1m2_ch1>; }; /* dp->serdes->lvds_panel */ From 0270f32f207f5682a729c17e977eb87bba83823e Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Fri, 8 Dec 2023 10:56:50 +0800 Subject: [PATCH 15/21] UPSTREAM: PCI: Move pci_clear_and_set_dword() helper to PCI header The clear and set pattern is commonly used for accessing PCI config, move the helper pci_clear_and_set_dword() from aspm.c into PCI header. In addition, rename to pci_clear_and_set_config_dword() to retain the "config" information and match the other accessors. No functional change intended. Signed-off-by: Shuai Xue Acked-by: Bjorn Helgaas Tested-by: Ilkka Koskinen Link: https://lore.kernel.org/r/20231208025652.87192-4-xueshuai@linux.alibaba.com Signed-off-by: Will Deacon (cherry-picked from ac16087134b837d42b75bb1c741070b6c142f258) Signed-off-by: Shawn Lin Change-Id: I35125190a4dd8ba25e6ec14b4712750605c22285 --- drivers/pci/access.c | 12 ++++++++ drivers/pci/pcie/aspm.c | 65 +++++++++++++++++++---------------------- include/linux/pci.h | 2 ++ 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 3d20f9c51efe..590294a9813d 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -594,3 +594,15 @@ int pci_write_config_dword(const struct pci_dev *dev, int where, return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_write_config_dword); + +void pci_clear_and_set_config_dword(const struct pci_dev *dev, int pos, + u32 clear, u32 set) +{ + u32 val; + + pci_read_config_dword(dev, pos, &val); + val &= ~clear; + val |= set; + pci_write_config_dword(dev, pos, val); +} +EXPORT_SYMBOL(pci_clear_and_set_config_dword); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 25736d408e88..9ba639f352e6 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -474,17 +474,6 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) } } -static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos, - u32 clear, u32 set) -{ - u32 val; - - pci_read_config_dword(pdev, pos, &val); - val &= ~clear; - val |= set; - pci_write_config_dword(pdev, pos, val); -} - /* Calculate L1.2 PM substate timing parameters */ static void aspm_calc_l1ss_info(struct pcie_link_state *link, u32 parent_l1ss_cap, u32 child_l1ss_cap) @@ -548,10 +537,12 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link, cl1_2_enables = cctl1 & PCI_L1SS_CTL1_L1_2_MASK; if (pl1_2_enables || cl1_2_enables) { - pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1_2_MASK, 0); - pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1_2_MASK, 0); + pci_clear_and_set_config_dword(child, + child->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1_2_MASK, 0); + pci_clear_and_set_config_dword(parent, + parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1_2_MASK, 0); } /* Program T_POWER_ON times in both ports */ @@ -559,22 +550,26 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link, pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2); /* Program Common_Mode_Restore_Time in upstream device */ - pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1); + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1); /* Program LTR_L1.2_THRESHOLD time in both ports */ - pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_LTR_L12_TH_VALUE | - PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1); - pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_LTR_L12_TH_VALUE | - PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1); + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_LTR_L12_TH_VALUE | + PCI_L1SS_CTL1_LTR_L12_TH_SCALE, + ctl1); + pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_LTR_L12_TH_VALUE | + PCI_L1SS_CTL1_LTR_L12_TH_SCALE, + ctl1); if (pl1_2_enables || cl1_2_enables) { - pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 0, - pl1_2_enables); - pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 0, - cl1_2_enables); + pci_clear_and_set_config_dword(parent, + parent->l1ss + PCI_L1SS_CTL1, 0, + pl1_2_enables); + pci_clear_and_set_config_dword(child, + child->l1ss + PCI_L1SS_CTL1, 0, + cl1_2_enables); } } @@ -734,10 +729,10 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) */ /* Disable all L1 substates */ - pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, 0); - pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, 0); + pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); /* * If needed, disable L1, and it gets enabled later * in pcie_config_aspm_link(). @@ -760,10 +755,10 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) val |= PCI_L1SS_CTL1_PCIPM_L1_2; /* Enable what we need to enable */ - pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, val); - pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, val); + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, val); + pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, val); } static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) diff --git a/include/linux/pci.h b/include/linux/pci.h index 0c80bdb145c5..f499731d170e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1208,6 +1208,8 @@ int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val); int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val); int pci_write_config_word(const struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val); +void pci_clear_and_set_config_dword(const struct pci_dev *dev, int pos, + u32 clear, u32 set); int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val); int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val); From 1b627c690ade9a72e3cd488e2e11edffb5d0e879 Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Fri, 8 Dec 2023 10:56:49 +0800 Subject: [PATCH 16/21] UPSTREAM: PCI: Add Alibaba Vendor ID to linux/pci_ids.h The Alibaba Vendor ID (0x1ded) is now used by Alibaba elasticRDMA ("erdma") and will be shared with the upcoming PCIe PMU ("dwc_pcie_pmu"). Move the Vendor ID to linux/pci_ids.h so that it can shared by several drivers later. Signed-off-by: Shuai Xue Acked-by: Bjorn Helgaas # pci_ids.h Tested-by: Ilkka Koskinen Link: https://lore.kernel.org/r/20231208025652.87192-3-xueshuai@linux.alibaba.com Signed-off-by: Will Deacon (cherry-picked from ad6534c626fedd818718d76c36d69c7d8e7b61cc) Signed-off-by: Shawn Lin Change-Id: I86188f119a42548ab777df0449f7d0a933f34d12 --- drivers/infiniband/hw/erdma/erdma_hw.h | 2 -- include/linux/pci_ids.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/erdma/erdma_hw.h b/drivers/infiniband/hw/erdma/erdma_hw.h index 2eb41e6d94c4..82bf66ca95b7 100644 --- a/drivers/infiniband/hw/erdma/erdma_hw.h +++ b/drivers/infiniband/hw/erdma/erdma_hw.h @@ -11,8 +11,6 @@ #include /* PCIe device related definition. */ -#define PCI_VENDOR_ID_ALIBABA 0x1ded - #define ERDMA_PCI_WIDTH 64 #define ERDMA_FUNC_BAR 0 #define ERDMA_MISX_BAR 2 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 70b1d8dde991..0106b8a0cd99 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2592,6 +2592,8 @@ #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 +#define PCI_VENDOR_ID_ALIBABA 0x1ded + #define PCI_VENDOR_ID_TEHUTI 0x1fc9 #define PCI_DEVICE_ID_TEHUTI_3009 0x3009 #define PCI_DEVICE_ID_TEHUTI_3010 0x3010 From dcfa6c8947baeac74ab44ea8f03d3831a062c14b Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Fri, 8 Dec 2023 10:56:51 +0800 Subject: [PATCH 17/21] BACKPORT: drivers/perf: add DesignWare PCIe PMU driver This commit adds the PCIe Performance Monitoring Unit (PMU) driver support for T-Head Yitian SoC chip. Yitian is based on the Synopsys PCI Express Core controller IP which provides statistics feature. The PMU is a PCIe configuration space register block provided by each PCIe Root Port in a Vendor-Specific Extended Capability named RAS D.E.S (Debug, Error injection, and Statistics). To facilitate collection of statistics the controller provides the following two features for each Root Port: - one 64-bit counter for Time Based Analysis (RX/TX data throughput and time spent in each low-power LTSSM state) and - one 32-bit counter for Event Counting (error and non-error events for a specified lane) Note: There is no interrupt for counter overflow. This driver adds PMU devices for each PCIe Root Port. And the PMU device is named based the BDF of Root Port. For example, 30:03.0 PCI bridge: Device 1ded:8000 (rev 01) the PMU device name for this Root Port is dwc_rootport_3018. Example usage of counting PCIe RX TLP data payload (Units of bytes):: $# perf stat -a -e dwc_rootport_3018/Rx_PCIe_TLP_Data_Payload/ average RX bandwidth can be calculated like this: PCIe TX Bandwidth = Rx_PCIe_TLP_Data_Payload / Measure_Time_Window Signed-off-by: Shuai Xue Reviewed-by: Baolin Wang Reviewed-by: Jonathan Cameron Reviewed-by: Yicong Yang Reviewed-and-tested-by: Ilkka Koskinen Link: https://lore.kernel.org/r/20231208025652.87192-5-xueshuai@linux.alibaba.com [will: Fix sparse error due to use of uninitialised 'vsec' symbol in dwc_pcie_match_des_cap()] Signed-off-by: Will Deacon (cherry-picked from af9597adc2f1e3609c67c9792a2469bb64e43ae9) Signed-off-by: Shawn Lin Change-Id: I470f4dc2791168760517c77dd31a4dacd7dab591 --- drivers/perf/Kconfig | 7 + drivers/perf/Makefile | 1 + drivers/perf/dwc_pcie_pmu.c | 791 ++++++++++++++++++++++++++++++++++++ 3 files changed, 799 insertions(+) create mode 100644 drivers/perf/dwc_pcie_pmu.c diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index 341010f20b77..79dcd2372a7a 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -199,4 +199,11 @@ config MARVELL_CN10K_DDR_PMU Enable perf support for Marvell DDR Performance monitoring event on CN10K platform. +config DWC_PCIE_PMU + tristate "Synopsys DesignWare PCIe PMU" + depends on PCI + help + Enable perf support for Synopsys DesignWare PCIe PMU Performance + monitoring event on platform including the Alibaba Yitian 710. + endmenu diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile index 050d04ee19dd..a742cb03aa01 100644 --- a/drivers/perf/Makefile +++ b/drivers/perf/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_MARVELL_CN10K_TAD_PMU) += marvell_cn10k_tad_pmu.o obj-$(CONFIG_MARVELL_CN10K_DDR_PMU) += marvell_cn10k_ddr_pmu.o obj-$(CONFIG_APPLE_M1_CPU_PMU) += apple_m1_cpu_pmu.o obj-$(CONFIG_ALIBABA_UNCORE_DRW_PMU) += alibaba_uncore_drw_pmu.o +obj-$(CONFIG_DWC_PCIE_PMU) += dwc_pcie_pmu.o diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c new file mode 100644 index 000000000000..ae7630ba82b8 --- /dev/null +++ b/drivers/perf/dwc_pcie_pmu.c @@ -0,0 +1,791 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare PCIe PMU driver + * + * Copyright (C) 2021-2023 Alibaba Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DWC_PCIE_VSEC_RAS_DES_ID 0x02 +#define DWC_PCIE_EVENT_CNT_CTL 0x8 + +/* + * Event Counter Data Select includes two parts: + * - 27-24: Group number(4-bit: 0..0x7) + * - 23-16: Event number(8-bit: 0..0x13) within the Group + * + * Put them together as in TRM. + */ +#define DWC_PCIE_CNT_EVENT_SEL GENMASK(27, 16) +#define DWC_PCIE_CNT_LANE_SEL GENMASK(11, 8) +#define DWC_PCIE_CNT_STATUS BIT(7) +#define DWC_PCIE_CNT_ENABLE GENMASK(4, 2) +#define DWC_PCIE_PER_EVENT_OFF 0x1 +#define DWC_PCIE_PER_EVENT_ON 0x3 +#define DWC_PCIE_EVENT_CLEAR GENMASK(1, 0) +#define DWC_PCIE_EVENT_PER_CLEAR 0x1 + +#define DWC_PCIE_EVENT_CNT_DATA 0xC + +#define DWC_PCIE_TIME_BASED_ANAL_CTL 0x10 +#define DWC_PCIE_TIME_BASED_REPORT_SEL GENMASK(31, 24) +#define DWC_PCIE_TIME_BASED_DURATION_SEL GENMASK(15, 8) +#define DWC_PCIE_DURATION_MANUAL_CTL 0x0 +#define DWC_PCIE_DURATION_1MS 0x1 +#define DWC_PCIE_DURATION_10MS 0x2 +#define DWC_PCIE_DURATION_100MS 0x3 +#define DWC_PCIE_DURATION_1S 0x4 +#define DWC_PCIE_DURATION_2S 0x5 +#define DWC_PCIE_DURATION_4S 0x6 +#define DWC_PCIE_DURATION_4US 0xFF +#define DWC_PCIE_TIME_BASED_TIMER_START BIT(0) +#define DWC_PCIE_TIME_BASED_CNT_ENABLE 0x1 + +#define DWC_PCIE_TIME_BASED_ANAL_DATA_REG_LOW 0x14 +#define DWC_PCIE_TIME_BASED_ANAL_DATA_REG_HIGH 0x18 + +/* Event attributes */ +#define DWC_PCIE_CONFIG_EVENTID GENMASK(15, 0) +#define DWC_PCIE_CONFIG_TYPE GENMASK(19, 16) +#define DWC_PCIE_CONFIG_LANE GENMASK(27, 20) + +#define DWC_PCIE_EVENT_ID(event) FIELD_GET(DWC_PCIE_CONFIG_EVENTID, (event)->attr.config) +#define DWC_PCIE_EVENT_TYPE(event) FIELD_GET(DWC_PCIE_CONFIG_TYPE, (event)->attr.config) +#define DWC_PCIE_EVENT_LANE(event) FIELD_GET(DWC_PCIE_CONFIG_LANE, (event)->attr.config) + +enum dwc_pcie_event_type { + DWC_PCIE_TIME_BASE_EVENT, + DWC_PCIE_LANE_EVENT, + DWC_PCIE_EVENT_TYPE_MAX, +}; + +#define DWC_PCIE_LANE_EVENT_MAX_PERIOD GENMASK_ULL(31, 0) +#define DWC_PCIE_MAX_PERIOD GENMASK_ULL(63, 0) + +struct dwc_pcie_pmu { + struct pmu pmu; + struct pci_dev *pdev; /* Root Port device */ + u16 ras_des_offset; + u32 nr_lanes; + + struct list_head pmu_node; + struct hlist_node cpuhp_node; + struct perf_event *event[DWC_PCIE_EVENT_TYPE_MAX]; + int on_cpu; +}; + +#define to_dwc_pcie_pmu(p) (container_of(p, struct dwc_pcie_pmu, pmu)) + +static int dwc_pcie_pmu_hp_state; +static struct list_head dwc_pcie_dev_info_head = + LIST_HEAD_INIT(dwc_pcie_dev_info_head); +static bool notify; + +struct dwc_pcie_dev_info { + struct platform_device *plat_dev; + struct pci_dev *pdev; + struct list_head dev_node; +}; + +struct dwc_pcie_vendor_id { + int vendor_id; +}; + +static const struct dwc_pcie_vendor_id dwc_pcie_vendor_ids[] = { + {.vendor_id = PCI_VENDOR_ID_ALIBABA }, + {} /* terminator */ +}; + +static ssize_t cpumask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_pmu->on_cpu)); +} +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *dwc_pcie_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL +}; + +static struct attribute_group dwc_pcie_cpumask_attr_group = { + .attrs = dwc_pcie_pmu_cpumask_attrs, +}; + +struct dwc_pcie_format_attr { + struct device_attribute attr; + u64 field; + int config; +}; + +PMU_FORMAT_ATTR(eventid, "config:0-15"); +PMU_FORMAT_ATTR(type, "config:16-19"); +PMU_FORMAT_ATTR(lane, "config:20-27"); + +static struct attribute *dwc_pcie_format_attrs[] = { + &format_attr_type.attr, + &format_attr_eventid.attr, + &format_attr_lane.attr, + NULL, +}; + +static struct attribute_group dwc_pcie_format_attrs_group = { + .name = "format", + .attrs = dwc_pcie_format_attrs, +}; + +struct dwc_pcie_event_attr { + struct device_attribute attr; + enum dwc_pcie_event_type type; + u16 eventid; + u8 lane; +}; + +static ssize_t dwc_pcie_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dwc_pcie_event_attr *eattr; + + eattr = container_of(attr, typeof(*eattr), attr); + + if (eattr->type == DWC_PCIE_LANE_EVENT) + return sysfs_emit(buf, "eventid=0x%x,type=0x%x,lane=?\n", + eattr->eventid, eattr->type); + else if (eattr->type == DWC_PCIE_TIME_BASE_EVENT) + return sysfs_emit(buf, "eventid=0x%x,type=0x%x\n", + eattr->eventid, eattr->type); + + return 0; +} + +#define DWC_PCIE_EVENT_ATTR(_name, _type, _eventid, _lane) \ + (&((struct dwc_pcie_event_attr[]) {{ \ + .attr = __ATTR(_name, 0444, dwc_pcie_event_show, NULL), \ + .type = _type, \ + .eventid = _eventid, \ + .lane = _lane, \ + }})[0].attr.attr) + +#define DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(_name, _eventid) \ + DWC_PCIE_EVENT_ATTR(_name, DWC_PCIE_TIME_BASE_EVENT, _eventid, 0) +#define DWC_PCIE_PMU_LANE_EVENT_ATTR(_name, _eventid) \ + DWC_PCIE_EVENT_ATTR(_name, DWC_PCIE_LANE_EVENT, _eventid, 0) + +static struct attribute *dwc_pcie_pmu_time_event_attrs[] = { + /* Group #0 */ + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(one_cycle, 0x00), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(TX_L0S, 0x01), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(RX_L0S, 0x02), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L0, 0x03), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1, 0x04), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_1, 0x05), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_2, 0x06), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(CFG_RCVRY, 0x07), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(TX_RX_L0S, 0x08), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_AUX, 0x09), + + /* Group #1 */ + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Tx_PCIe_TLP_Data_Payload, 0x20), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Rx_PCIe_TLP_Data_Payload, 0x21), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Tx_CCIX_TLP_Data_Payload, 0x22), + DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Rx_CCIX_TLP_Data_Payload, 0x23), + + /* + * Leave it to the user to specify the lane ID to avoid generating + * a list of hundreds of events. + */ + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_ack_dllp, 0x600), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_update_fc_dllp, 0x601), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_ack_dllp, 0x602), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_update_fc_dllp, 0x603), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_nulified_tlp, 0x604), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_nulified_tlp, 0x605), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_duplicate_tl, 0x606), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_memory_write, 0x700), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_memory_read, 0x701), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_configuration_write, 0x702), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_configuration_read, 0x703), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_io_write, 0x704), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_io_read, 0x705), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_completion_without_data, 0x706), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_completion_with_data, 0x707), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_message_tlp, 0x708), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_atomic, 0x709), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_tlp_with_prefix, 0x70A), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_memory_write, 0x70B), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_memory_read, 0x70C), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_io_write, 0x70F), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_io_read, 0x710), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_completion_without_data, 0x711), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_completion_with_data, 0x712), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_message_tlp, 0x713), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_atomic, 0x714), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_tlp_with_prefix, 0x715), + DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_ccix_tlp, 0x716), + DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_ccix_tlp, 0x717), + NULL +}; + +static const struct attribute_group dwc_pcie_event_attrs_group = { + .name = "events", + .attrs = dwc_pcie_pmu_time_event_attrs, +}; + +static const struct attribute_group *dwc_pcie_attr_groups[] = { + &dwc_pcie_event_attrs_group, + &dwc_pcie_format_attrs_group, + &dwc_pcie_cpumask_attr_group, + NULL +}; + +static void dwc_pcie_pmu_lane_event_enable(struct dwc_pcie_pmu *pcie_pmu, + bool enable) +{ + struct pci_dev *pdev = pcie_pmu->pdev; + u16 ras_des_offset = pcie_pmu->ras_des_offset; + + if (enable) + pci_clear_and_set_config_dword(pdev, + ras_des_offset + DWC_PCIE_EVENT_CNT_CTL, + DWC_PCIE_CNT_ENABLE, DWC_PCIE_PER_EVENT_ON); + else + pci_clear_and_set_config_dword(pdev, + ras_des_offset + DWC_PCIE_EVENT_CNT_CTL, + DWC_PCIE_CNT_ENABLE, DWC_PCIE_PER_EVENT_OFF); +} + +static void dwc_pcie_pmu_time_based_event_enable(struct dwc_pcie_pmu *pcie_pmu, + bool enable) +{ + struct pci_dev *pdev = pcie_pmu->pdev; + u16 ras_des_offset = pcie_pmu->ras_des_offset; + + pci_clear_and_set_config_dword(pdev, + ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_CTL, + DWC_PCIE_TIME_BASED_TIMER_START, enable); +} + +static u64 dwc_pcie_pmu_read_lane_event_counter(struct perf_event *event) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + struct pci_dev *pdev = pcie_pmu->pdev; + u16 ras_des_offset = pcie_pmu->ras_des_offset; + u32 val; + + pci_read_config_dword(pdev, ras_des_offset + DWC_PCIE_EVENT_CNT_DATA, &val); + + return val; +} + +static u64 dwc_pcie_pmu_read_time_based_counter(struct perf_event *event) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + struct pci_dev *pdev = pcie_pmu->pdev; + int event_id = DWC_PCIE_EVENT_ID(event); + u16 ras_des_offset = pcie_pmu->ras_des_offset; + u32 lo, hi, ss; + u64 val; + + /* + * The 64-bit value of the data counter is spread across two + * registers that are not synchronized. In order to read them + * atomically, ensure that the high 32 bits match before and after + * reading the low 32 bits. + */ + pci_read_config_dword(pdev, + ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_DATA_REG_HIGH, &hi); + do { + /* snapshot the high 32 bits */ + ss = hi; + + pci_read_config_dword( + pdev, ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_DATA_REG_LOW, + &lo); + pci_read_config_dword( + pdev, ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_DATA_REG_HIGH, + &hi); + } while (hi != ss); + + val = ((u64)hi << 32) | lo; + /* + * The Group#1 event measures the amount of data processed in 16-byte + * units. Simplify the end-user interface by multiplying the counter + * at the point of read. + */ + if (event_id >= 0x20 && event_id <= 0x23) + val *= 16; + + return val; +} + +static void dwc_pcie_pmu_event_update(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event); + u64 delta, prev, now = 0; + + do { + prev = local64_read(&hwc->prev_count); + + if (type == DWC_PCIE_LANE_EVENT) + now = dwc_pcie_pmu_read_lane_event_counter(event); + else if (type == DWC_PCIE_TIME_BASE_EVENT) + now = dwc_pcie_pmu_read_time_based_counter(event); + + } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev); + + delta = (now - prev) & DWC_PCIE_MAX_PERIOD; + /* 32-bit counter for Lane Event Counting */ + if (type == DWC_PCIE_LANE_EVENT) + delta &= DWC_PCIE_LANE_EVENT_MAX_PERIOD; + + local64_add(delta, &event->count); +} + +static int dwc_pcie_pmu_event_init(struct perf_event *event) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event); + struct perf_event *sibling; + u32 lane; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + /* We don't support sampling */ + if (is_sampling_event(event)) + return -EINVAL; + + /* We cannot support task bound events */ + if (event->cpu < 0 || event->attach_state & PERF_ATTACH_TASK) + return -EINVAL; + + if (event->group_leader != event && + !is_software_event(event->group_leader)) + return -EINVAL; + + for_each_sibling_event(sibling, event->group_leader) { + if (sibling->pmu != event->pmu && !is_software_event(sibling)) + return -EINVAL; + } + + if (type < 0 || type >= DWC_PCIE_EVENT_TYPE_MAX) + return -EINVAL; + + if (type == DWC_PCIE_LANE_EVENT) { + lane = DWC_PCIE_EVENT_LANE(event); + if (lane < 0 || lane >= pcie_pmu->nr_lanes) + return -EINVAL; + } + + event->cpu = pcie_pmu->on_cpu; + + return 0; +} + +static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event); + + hwc->state = 0; + local64_set(&hwc->prev_count, 0); + + if (type == DWC_PCIE_LANE_EVENT) + dwc_pcie_pmu_lane_event_enable(pcie_pmu, true); + else if (type == DWC_PCIE_TIME_BASE_EVENT) + dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true); +} + +static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event); + struct hw_perf_event *hwc = &event->hw; + + if (event->hw.state & PERF_HES_STOPPED) + return; + + if (type == DWC_PCIE_LANE_EVENT) + dwc_pcie_pmu_lane_event_enable(pcie_pmu, false); + else if (type == DWC_PCIE_TIME_BASE_EVENT) + dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false); + + dwc_pcie_pmu_event_update(event); + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; +} + +static int dwc_pcie_pmu_event_add(struct perf_event *event, int flags) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + struct pci_dev *pdev = pcie_pmu->pdev; + struct hw_perf_event *hwc = &event->hw; + enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event); + int event_id = DWC_PCIE_EVENT_ID(event); + int lane = DWC_PCIE_EVENT_LANE(event); + u16 ras_des_offset = pcie_pmu->ras_des_offset; + u32 ctrl; + + /* one counter for each type and it is in use */ + if (pcie_pmu->event[type]) + return -ENOSPC; + + pcie_pmu->event[type] = event; + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + + if (type == DWC_PCIE_LANE_EVENT) { + /* EVENT_COUNTER_DATA_REG needs clear manually */ + ctrl = FIELD_PREP(DWC_PCIE_CNT_EVENT_SEL, event_id) | + FIELD_PREP(DWC_PCIE_CNT_LANE_SEL, lane) | + FIELD_PREP(DWC_PCIE_CNT_ENABLE, DWC_PCIE_PER_EVENT_OFF) | + FIELD_PREP(DWC_PCIE_EVENT_CLEAR, DWC_PCIE_EVENT_PER_CLEAR); + pci_write_config_dword(pdev, ras_des_offset + DWC_PCIE_EVENT_CNT_CTL, + ctrl); + } else if (type == DWC_PCIE_TIME_BASE_EVENT) { + /* + * TIME_BASED_ANAL_DATA_REG is a 64 bit register, we can safely + * use it with any manually controlled duration. And it is + * cleared when next measurement starts. + */ + ctrl = FIELD_PREP(DWC_PCIE_TIME_BASED_REPORT_SEL, event_id) | + FIELD_PREP(DWC_PCIE_TIME_BASED_DURATION_SEL, + DWC_PCIE_DURATION_MANUAL_CTL) | + DWC_PCIE_TIME_BASED_CNT_ENABLE; + pci_write_config_dword( + pdev, ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_CTL, ctrl); + } + + if (flags & PERF_EF_START) + dwc_pcie_pmu_event_start(event, PERF_EF_RELOAD); + + perf_event_update_userpage(event); + + return 0; +} + +static void dwc_pcie_pmu_event_del(struct perf_event *event, int flags) +{ + struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu); + enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event); + + dwc_pcie_pmu_event_stop(event, flags | PERF_EF_UPDATE); + perf_event_update_userpage(event); + pcie_pmu->event[type] = NULL; +} + +static void dwc_pcie_pmu_remove_cpuhp_instance(void *hotplug_node) +{ + cpuhp_state_remove_instance_nocalls(dwc_pcie_pmu_hp_state, hotplug_node); +} + +/* + * Find the binded DES capability device info of a PCI device. + * @pdev: The PCI device. + */ +static struct dwc_pcie_dev_info *dwc_pcie_find_dev_info(struct pci_dev *pdev) +{ + struct dwc_pcie_dev_info *dev_info; + + list_for_each_entry(dev_info, &dwc_pcie_dev_info_head, dev_node) + if (dev_info->pdev == pdev) + return dev_info; + + return NULL; +} + +static void dwc_pcie_unregister_pmu(void *data) +{ + struct dwc_pcie_pmu *pcie_pmu = data; + + perf_pmu_unregister(&pcie_pmu->pmu); +} + +static bool dwc_pcie_match_des_cap(struct pci_dev *pdev) +{ + const struct dwc_pcie_vendor_id *vid; + u16 vsec = 0; + u32 val; + + if (!pci_is_pcie(pdev) || !(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)) + return false; + + for (vid = dwc_pcie_vendor_ids; vid->vendor_id; vid++) { + vsec = pci_find_vsec_capability(pdev, vid->vendor_id, + DWC_PCIE_VSEC_RAS_DES_ID); + if (vsec) + break; + } + if (!vsec) + return false; + + pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val); + if (PCI_VNDR_HEADER_REV(val) != 0x04) + return false; + + pci_dbg(pdev, + "Detected PCIe Vendor-Specific Extended Capability RAS DES\n"); + return true; +} + +static void dwc_pcie_unregister_dev(struct dwc_pcie_dev_info *dev_info) +{ + platform_device_unregister(dev_info->plat_dev); + list_del(&dev_info->dev_node); + kfree(dev_info); +} + +static int dwc_pcie_register_dev(struct pci_dev *pdev) +{ + struct platform_device *plat_dev; + struct dwc_pcie_dev_info *dev_info; + u32 bdf; + + bdf = PCI_DEVID(pdev->bus->number, pdev->devfn); + plat_dev = platform_device_register_data(NULL, "dwc_pcie_pmu", bdf, + pdev, sizeof(*pdev)); + + if (IS_ERR(plat_dev)) + return PTR_ERR(plat_dev); + + dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) + return -ENOMEM; + + /* Cache platform device to handle pci device hotplug */ + dev_info->plat_dev = plat_dev; + dev_info->pdev = pdev; + list_add(&dev_info->dev_node, &dwc_pcie_dev_info_head); + + return 0; +} + +static int dwc_pcie_pmu_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct pci_dev *pdev = to_pci_dev(dev); + struct dwc_pcie_dev_info *dev_info; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + if (!dwc_pcie_match_des_cap(pdev)) + return NOTIFY_DONE; + if (dwc_pcie_register_dev(pdev)) + return NOTIFY_BAD; + break; + case BUS_NOTIFY_DEL_DEVICE: + dev_info = dwc_pcie_find_dev_info(pdev); + if (!dev_info) + return NOTIFY_DONE; + dwc_pcie_unregister_dev(dev_info); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block dwc_pcie_pmu_nb = { + .notifier_call = dwc_pcie_pmu_notifier, +}; + +static int dwc_pcie_pmu_probe(struct platform_device *plat_dev) +{ + struct pci_dev *pdev = plat_dev->dev.platform_data; + struct dwc_pcie_pmu *pcie_pmu; + char *name; + u32 bdf, val; + u16 vsec; + int ret; + + vsec = pci_find_vsec_capability(pdev, pdev->vendor, + DWC_PCIE_VSEC_RAS_DES_ID); + pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val); + bdf = PCI_DEVID(pdev->bus->number, pdev->devfn); + name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", bdf); + if (!name) + return -ENOMEM; + + pcie_pmu = devm_kzalloc(&plat_dev->dev, sizeof(*pcie_pmu), GFP_KERNEL); + if (!pcie_pmu) + return -ENOMEM; + + pcie_pmu->pdev = pdev; + pcie_pmu->ras_des_offset = vsec; + pcie_pmu->nr_lanes = pcie_get_width_cap(pdev); + pcie_pmu->on_cpu = -1; + pcie_pmu->pmu = (struct pmu){ + .name = name, + .module = THIS_MODULE, + .attr_groups = dwc_pcie_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + .task_ctx_nr = perf_invalid_context, + .event_init = dwc_pcie_pmu_event_init, + .add = dwc_pcie_pmu_event_add, + .del = dwc_pcie_pmu_event_del, + .start = dwc_pcie_pmu_event_start, + .stop = dwc_pcie_pmu_event_stop, + .read = dwc_pcie_pmu_event_update, + }; + + /* Add this instance to the list used by the offline callback */ + ret = cpuhp_state_add_instance(dwc_pcie_pmu_hp_state, + &pcie_pmu->cpuhp_node); + if (ret) { + pci_err(pdev, "Error %d registering hotplug @%x\n", ret, bdf); + return ret; + } + + /* Unwind when platform driver removes */ + ret = devm_add_action_or_reset(&plat_dev->dev, + dwc_pcie_pmu_remove_cpuhp_instance, + &pcie_pmu->cpuhp_node); + if (ret) + return ret; + + ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); + if (ret) { + pci_err(pdev, "Error %d registering PMU @%x\n", ret, bdf); + return ret; + } + ret = devm_add_action_or_reset(&plat_dev->dev, dwc_pcie_unregister_pmu, + pcie_pmu); + if (ret) + return ret; + + return 0; +} + +static int dwc_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *cpuhp_node) +{ + struct dwc_pcie_pmu *pcie_pmu; + + pcie_pmu = hlist_entry_safe(cpuhp_node, struct dwc_pcie_pmu, cpuhp_node); + if (pcie_pmu->on_cpu == -1) + pcie_pmu->on_cpu = cpumask_local_spread( + 0, dev_to_node(&pcie_pmu->pdev->dev)); + + return 0; +} + +static int dwc_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_node) +{ + struct dwc_pcie_pmu *pcie_pmu; + struct pci_dev *pdev; + int node; + cpumask_t mask; + unsigned int target; + + pcie_pmu = hlist_entry_safe(cpuhp_node, struct dwc_pcie_pmu, cpuhp_node); + /* Nothing to do if this CPU doesn't own the PMU */ + if (cpu != pcie_pmu->on_cpu) + return 0; + + pcie_pmu->on_cpu = -1; + pdev = pcie_pmu->pdev; + node = dev_to_node(&pdev->dev); + if (cpumask_and(&mask, cpumask_of_node(node), cpu_online_mask) && + cpumask_andnot(&mask, &mask, cpumask_of(cpu))) + target = cpumask_any(&mask); + else + target = cpumask_any_but(cpu_online_mask, cpu); + + if (target >= nr_cpu_ids) { + pci_err(pdev, "There is no CPU to set\n"); + return 0; + } + + /* This PMU does NOT support interrupt, just migrate context. */ + perf_pmu_migrate_context(&pcie_pmu->pmu, cpu, target); + pcie_pmu->on_cpu = target; + + return 0; +} + +static struct platform_driver dwc_pcie_pmu_driver = { + .probe = dwc_pcie_pmu_probe, + .driver = {.name = "dwc_pcie_pmu",}, +}; + +static int __init dwc_pcie_pmu_init(void) +{ + struct pci_dev *pdev = NULL; + bool found = false; + int ret; + + for_each_pci_dev(pdev) { + if (!dwc_pcie_match_des_cap(pdev)) + continue; + + ret = dwc_pcie_register_dev(pdev); + if (ret) { + pci_dev_put(pdev); + return ret; + } + + found = true; + } + if (!found) + return -ENODEV; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/dwc_pcie_pmu:online", + dwc_pcie_pmu_online_cpu, + dwc_pcie_pmu_offline_cpu); + if (ret < 0) + return ret; + + dwc_pcie_pmu_hp_state = ret; + + ret = platform_driver_register(&dwc_pcie_pmu_driver); + if (ret) + goto platform_driver_register_err; + + ret = bus_register_notifier(&pci_bus_type, &dwc_pcie_pmu_nb); + if (ret) + goto platform_driver_register_err; + notify = true; + + return 0; + +platform_driver_register_err: + cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state); + + return ret; +} + +static void __exit dwc_pcie_pmu_exit(void) +{ + struct dwc_pcie_dev_info *dev_info, *tmp; + + if (notify) + bus_unregister_notifier(&pci_bus_type, &dwc_pcie_pmu_nb); + list_for_each_entry_safe(dev_info, tmp, &dwc_pcie_dev_info_head, dev_node) + dwc_pcie_unregister_dev(dev_info); + platform_driver_unregister(&dwc_pcie_pmu_driver); + cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state); +} + +module_init(dwc_pcie_pmu_init); +module_exit(dwc_pcie_pmu_exit); + +MODULE_DESCRIPTION("PMU driver for DesignWare Cores PCI Express Controller"); +MODULE_AUTHOR("Shuai Xue "); +MODULE_LICENSE("GPL v2"); From 6cb6a00862fa29f815412634569e2015f86e397a Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 3 Sep 2024 16:24:36 +0800 Subject: [PATCH 18/21] perf/dwc_pcie: Add support for Rockchip vendor devices Signed-off-by: Shawn Lin Change-Id: I6fde80440d2fa058b38a7d927eb846f477812b5f --- drivers/perf/dwc_pcie_pmu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/perf/dwc_pcie_pmu.c b/drivers/perf/dwc_pcie_pmu.c index ae7630ba82b8..c0b8822e87bd 100644 --- a/drivers/perf/dwc_pcie_pmu.c +++ b/drivers/perf/dwc_pcie_pmu.c @@ -107,6 +107,7 @@ struct dwc_pcie_vendor_id { static const struct dwc_pcie_vendor_id dwc_pcie_vendor_ids[] = { {.vendor_id = PCI_VENDOR_ID_ALIBABA }, + {.vendor_id = PCI_VENDOR_ID_ROCKCHIP }, {} /* terminator */ }; From 50cb3fcd18fb9defe23ba95eb3962a287e957166 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 3 Sep 2024 16:24:36 +0800 Subject: [PATCH 19/21] PCI: rockchip: dw: Add dwc pmu support for rockchip Remove thread init if using DWC_PCIE_PMU, because late pcie bus scanning would miss probing from dwc_pcie_pmu_init(). Signed-off-by: Shawn Lin Change-Id: Ia27ee055aa3e63deeb7fd646411c3542b7019288 --- drivers/pci/controller/dwc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 7948cd11e2de..a9f528f1d842 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -92,6 +92,7 @@ config PCIE_DW_ROCKCHIP config PCIE_RK_THREADED_INIT bool "Threaded initialize Rockchip DW based PCIe controller" depends on PCIE_DW_ROCKCHIP + depends on !DWC_PCIE_PMU default y help Enables threaded initialize Rockchip DW based PCIe controller. From 37e6a4d881dd8c4dce22490e4746d4f01dde4247 Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Sat, 21 Sep 2024 17:24:30 +0800 Subject: [PATCH 20/21] arm64: dts: rockchip: Add sy7636a power good pin for rk3576-ebook-v10 Change-Id: I08c20297b17c72e73f161d0d6c2ee8b4ccf2fad9 Signed-off-by: Chaoyi Chen --- arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts b/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts index 86014f2f63ec..5c233d9a8c58 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts @@ -493,6 +493,7 @@ pinctrl-0 = <&sy7636a_default>; enable-gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>, <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>; + pgood-gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>; thermal-zone = "ebcpmic-thermal"; #thermal-sensor-cells = <0>; @@ -747,7 +748,8 @@ sy7636a_default: sy7636a_default { rockchip,pins = <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>, - <2 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>; + <2 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>; }; }; From 6d08fe13929b0093002b716aadcf33534def212c Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Mon, 15 Jul 2024 16:47:42 +0800 Subject: [PATCH 21/21] arm64: dts: rockchip: Add fp9931 pmic config for rk3576-ebook-v10 Signed-off-by: Chaoyi Chen Change-Id: I436749871368488b5e80f178d1b4302f2f34c17b --- .../boot/dts/rockchip/rk3576-ebook-v10.dts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts b/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts index 5c233d9a8c58..569b175d6021 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-ebook-v10.dts @@ -484,6 +484,49 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c7m2_xfer>; + fp9931: fp9931@18 { + compatible = "fitipower,fp9931-pmic"; + reg = <0x18>; + status = "disabled"; + + pinctrl-names = "default"; + pinctrl-0 = <&fp9931_default>; + + /* fp9931 EN_TS(pin15) and external power control pin */ + power-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>, <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>; + /* fp9931 EN(pin16) */ + enable-gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>; + + thermal-zone = "fp9931-pmic-thermal"; + #thermal-sensor-cells = <0>; + + regulators { + compatible = "fp9931-regulator"; + fp9931_vcom: vcom { + regulator-name = "vcom"; + regulator-min-microvolt = <0>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + fp9931_vpos_vneg: vpos_vneg { + regulator-name = "vpos_vneg"; + regulator-init-microvolt = <15060000>; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + + fp9931_thermal: fp9931-thermal { + compatible = "fp9931-thermal"; + }; + }; + sy7636a: sy7636a@62 { compatible = "silergy,sy7636a-pmic"; reg = <0x62>; @@ -724,6 +767,15 @@ }; }; + fp9931 { + fp9931_default: fp9931-default { + rockchip,pins = + <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + hall { mh248_irq_gpio: mh248-irq-gpio { rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_up>; @@ -903,6 +955,20 @@ }; }; }; + + fp9931_pmic_thermal: fp9931-pmic-thermal { + polling-delay = <0>; + polling-delay-passive = <0>; + thermal-sensors = <&fp9931>; + + trips { + fp9931_dummy_trip: fp9931-dummy-trip { + temperature = <100000>; + hysteresis = <0>; + type = "hot"; + }; + }; + }; }; &tsadc {