* RE: [RFC PATCH 18/18] net: wireguard - switch to crypto API for packet encryption
From: Pascal Van Leeuwen @ 2019-09-26 9:40 UTC (permalink / raw)
To: Linus Torvalds, Ard Biesheuvel
Cc: Jason A . Donenfeld, Catalin Marinas, Herbert Xu, Arnd Bergmann,
Eric Biggers, Greg KH, Samuel Neves, Linux Crypto Mailing List,
Andy Lutomirski, Marc Zyngier, Dan Carpenter, Will Deacon,
David Miller, Linux ARM
In-Reply-To: <CAHk-=wjYsbxSiV_XKWV3BwGvau_hUvQiQHLOoc7vLUZt0Wqzfw@mail.gmail.com>
> >
> > Replace the chacha20poly1305() library calls with invocations of the
> > RFC7539 AEAD, as implemented by the generic chacha20poly1305 template.
>
> Honestly, the other patches look fine to me from what I've seen (with
> the small note I had in a separate email for 11/18), but this one I
> consider just nasty, and a prime example of why people hate those
> crypto lookup routines.
>
> Some of it is just the fundamental and pointless silly indirection,
> that just makes things harder to read, less efficient, and less
> straightforward.
>
> That's exemplified by this part of the patch:
>
> > struct noise_symmetric_key {
> > - u8 key[NOISE_SYMMETRIC_KEY_LEN];
> > + struct crypto_aead *tfm;
>
> which is just one of those "we know what we want and we just want to
> use it directly" things, and then the crypto indirection comes along
> and makes that simple inline allocation of a small constant size
> (afaik it is CHACHA20POLY1305_KEY_SIZE, which is 32) be another
> allocation entirely.
>
> And it's some random odd non-typed thing too, so then you have that
> silly and stupid dynamic allocation using a name lookup:
>
> crypto_alloc_aead("rfc7539(chacha20,poly1305)", 0, CRYPTO_ALG_ASYNC);
>
> to create what used to be (and should be) a simple allocation that was
> has a static type and was just part of the code.
>
While I agree with the principle of first merging Wireguard without
hooking it up to the Crypto API and doing the latter in a later,
separate patch, I DONT'T agree with your bashing of the Crypto API
or HW crypto acceleration in general.
Yes, I do agree that if you need to do the occasional single crypto
op for a fixed algorithm on a small amount of data then you should
just use a simple direct library call. I'm all for a Zinc type
library for that.
(and I believe Ard is actually actively making such changes already)
However, if you're doing bulk crypto like network packet processing
(as Wireguard does!) or disk/filesystem encryption, then that cipher
allocation only happens once every blue moon and the overhead for
that is totally *irrelevant* as it is amortized over many hours or
days of runtime.
While I generally dislike this whole hype of storing stuff in
textual formats like XML and JSON and then wasting lots of CPU
cycles on parsing that, I've learned to appreciate the power of
these textual Crypto API templates, as they allow a hardware
accelerator to advertise complex combined operations as single
atomic calls, amortizing the communication overhead between SW
and HW. It's actually very flexible and powerful!
> It also ends up doing other bad things, ie that packet-time
>
> + if (unlikely(crypto_aead_reqsize(key->tfm) > 0)) {
> + req = aead_request_alloc(key->tfm, GFP_ATOMIC);
> + if (!req)
> + return false;
>
> thing that hopefully _is_ unlikely, but that's just more potential
> breakage from that whole async crypto interface.
>
> This is what people do *not* want to do, and why people often don't
> like the crypto interfaces.
>
Life is all about needing to do things you don't like to do ...
If you want the performance, you need to do the effort. That simple.
HW acceleration surely won't work from a naive synchronous interface.
(Same applies to running crypto in a separate thread on the CPU BTW!)
In any case, Wireguard bulk crypto *should* eventually run on top
of Crypto API such that it can leverage *existing* HW acceleration.
It would be incredibly silly not to do so, given the HW exists!
> And I'm still not convinced (a) ever makes sense - the overhead of any
> accelerator is just high enought that I doubt you'll have numbers -
> performance _or_ power.
>
You shouldn't make such assertions if you obviously don't know what
you're talking about. Yes, there is significant overhead on the CPU
for doing lookaside crypto, but it's (usually) nothing compared to
doing the actual crypto itself on the CPU barring a few exceptions.
(Notably AES-GCM or AES-CTR on ARM64 or x64 CPU's and *maybe*
Chacha-Poly on recent Intel CPU's - but there's a *lot* more crypto
being used out there than just AES-GCM and Chacha-Poly, not to
mention a lot more less capable (embedded) CPU's running Linux)
For anything but those exceptions, we blow even the fastest Intel
server CPU's out of the water with our crypto accelerators.
(I can bore you with some figures actually measured with the
Crypto API on our HW, once I'm done optimizing the driver and I
have some time to collect the results)
And in any case, for somewhat larger blocks/packets, the overhead
on the CPU would at least be such that it's less than what the CPU
would need to do the crypto itself - even if it's faster - such that
there is room there to do *other*, presumably more useful, work.
Then there's indeed the power consumption issue, which is complex
because crypto power != total system power so it depends too much on
the actual use case to make generic statements on it. So I'll leave
that with the remark that Intel server CPU's have to seriously
throttle down their clock if you start using AVX512 for crypto, just to
stay within their power budget, while we can do the same performance
(~200 Gbps) in just a few (~2) Watts on a similar technology node.
(excluding the CPU management overhead, but that surely won't consume
excessive power like AVX512)
> But even if you're right that it might be a power advantage on some
> platform, that wouldn't make it an advantage on other platforms. Maybe
> it could be done as a config option where you can opt in to the async
> interface when that makes sense - but not force the indirection and
> extra allocations when it doesn't. As a separate patch, something like
> that doesn't sound horrendous (and I think that's also an argument for
> doing that CPU->LE change as an independent change).
>
Making it a switch sounds good to me though.
Regards,
Pascal van Leeuwen
Silicon IP Architect, Multi-Protocol Engines @ Verimatrix
www.insidesecure.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 v3 5/5] media: platform: Add jpeg dec/enc feature
From: Hans Verkuil @ 2019-09-26 9:46 UTC (permalink / raw)
To: Xia Jiang, Mauro Carvalho Chehab, Rob Herring, Matthias Brugger,
Rick Chang
Cc: devicetree, srv_heupstream, linux-kernel, Tomasz Figa,
linux-mediatek, linux-media, linux-arm-kernel, Marek Szyprowski
In-Reply-To: <20190924074303.22713-6-xia.jiang@mediatek.com>
On 9/24/19 9:43 AM, Xia Jiang wrote:
> Add mtk jpeg encode v4l2 driver based on jpeg decode, because that jpeg
> decode and encode have great similarities with function operation.
>
> Signed-off-by: Xia Jiang <xia.jiang@mediatek.com>
> ---
> v3: delete Change-Id
> only test once handler->error after the last v4l2_ctrl_new_std()
> seperate changes of v4l2-ctrls.c and v4l2-controls.h to new patch
> v2: fix compliance test fail, check created buffer size in driver
> ---
> drivers/media/platform/mtk-jpeg/Makefile | 5 +-
> .../media/platform/mtk-jpeg/mtk_jpeg_core.c | 735 ++++++++++++++----
> .../media/platform/mtk-jpeg/mtk_jpeg_core.h | 114 ++-
> .../media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h | 7 +-
> .../media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c | 175 +++++
> .../media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h | 60 ++
> .../platform/mtk-jpeg/mtk_jpeg_enc_reg.h | 49 ++
> 7 files changed, 975 insertions(+), 170 deletions(-)
> create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c
> create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h
> create mode 100644 drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_reg.h
>
> diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mtk-jpeg/Makefile
> index 48516dcf96e6..76c33aad0f3f 100644
> --- a/drivers/media/platform/mtk-jpeg/Makefile
> +++ b/drivers/media/platform/mtk-jpeg/Makefile
> @@ -1,3 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0-only
> -mtk_jpeg-objs := mtk_jpeg_core.o mtk_jpeg_dec_hw.o mtk_jpeg_dec_parse.o
> +mtk_jpeg-objs := mtk_jpeg_core.o \
> + mtk_jpeg_dec_hw.o \
> + mtk_jpeg_dec_parse.o \
> + mtk_jpeg_enc_hw.o
> obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk_jpeg.o
> diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> index 5f0990fce8ef..aa18b01802ed 100644
> --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
> @@ -3,6 +3,7 @@
> * Copyright (c) 2016 MediaTek Inc.
> * Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com>
> * Rick Chang <rick.chang@mediatek.com>
> + * Xia Jiang <xia.jiang@mediatek.com>
> */
>
> #include <linux/clk.h>
> @@ -23,6 +24,7 @@
> #include <media/videobuf2-dma-contig.h>
> #include <soc/mediatek/smi.h>
>
> +#include "mtk_jpeg_enc_hw.h"
> #include "mtk_jpeg_dec_hw.h"
> #include "mtk_jpeg_core.h"
> #include "mtk_jpeg_dec_parse.h"
> @@ -31,7 +33,8 @@ static struct mtk_jpeg_fmt mtk_jpeg_formats[] = {
> {
> .fourcc = V4L2_PIX_FMT_JPEG,
> .colplanes = 1,
> - .flags = MTK_JPEG_FMT_FLAG_DEC_OUTPUT,
> + .flags = MTK_JPEG_FMT_FLAG_DEC_OUTPUT |
> + MTK_JPEG_FMT_FLAG_ENC_CAPTURE,
> },
> {
> .fourcc = V4L2_PIX_FMT_YUV420M,
> @@ -51,6 +54,42 @@ static struct mtk_jpeg_fmt mtk_jpeg_formats[] = {
> .v_align = 3,
> .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE,
> },
> + {
> + .fourcc = V4L2_PIX_FMT_NV12M,
> + .h_sample = {4, 2, 2},
> + .v_sample = {4, 2, 2},
> + .colplanes = 2,
> + .h_align = 4,
> + .v_align = 4,
> + .flags = MTK_JPEG_FMT_FLAG_ENC_OUTPUT,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_NV21M,
> + .h_sample = {4, 2, 2},
> + .v_sample = {4, 2, 2},
> + .colplanes = 2,
> + .h_align = 4,
> + .v_align = 4,
> + .flags = MTK_JPEG_FMT_FLAG_ENC_OUTPUT,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_YUYV,
> + .h_sample = {4, 2, 2},
> + .v_sample = {4, 4, 4},
> + .colplanes = 1,
> + .h_align = 4,
> + .v_align = 3,
> + .flags = MTK_JPEG_FMT_FLAG_ENC_OUTPUT,
> + },
> + {
> + .fourcc = V4L2_PIX_FMT_YVYU,
> + .h_sample = {4, 2, 2},
> + .v_sample = {4, 4, 4},
> + .colplanes = 1,
> + .h_align = 4,
> + .v_align = 3,
> + .flags = MTK_JPEG_FMT_FLAG_ENC_OUTPUT,
> + },
> };
>
> #define MTK_JPEG_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_formats)
> @@ -65,11 +104,19 @@ struct mtk_jpeg_src_buf {
> struct list_head list;
> int flags;
> struct mtk_jpeg_dec_param dec_param;
> + struct mtk_jpeg_enc_param enc_param;
> };
>
> +#define MTK_MAX_CTRLS_HINT 20
> +
> static int debug;
> module_param(debug, int, 0644);
>
> +static inline struct mtk_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
> +{
> + return container_of(ctrl->handler, struct mtk_jpeg_ctx, ctrl_hdl);
> +}
> +
> static inline struct mtk_jpeg_ctx *mtk_jpeg_fh_to_ctx(struct v4l2_fh *fh)
> {
> return container_of(fh, struct mtk_jpeg_ctx, fh);
> @@ -86,10 +133,70 @@ static int mtk_jpeg_querycap(struct file *file, void *priv,
> {
> struct mtk_jpeg_dev *jpeg = video_drvdata(file);
>
> - strscpy(cap->driver, MTK_JPEG_NAME " decoder", sizeof(cap->driver));
> - strscpy(cap->card, MTK_JPEG_NAME " decoder", sizeof(cap->card));
> - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> - dev_name(jpeg->dev));
> + strscpy(cap->driver, MTK_JPEG_NAME, sizeof(cap->driver));
> + if (jpeg->mode == MTK_JPEG_ENC)
> + strscpy(cap->card, MTK_JPEG_NAME " encoder", sizeof(cap->card));
> + else
> + strscpy(cap->card, MTK_JPEG_NAME " decoder", sizeof(cap->card));
> + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> + dev_name(jpeg->dev));
> +
> + return 0;
> +}
> +
> +static int vidioc_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct mtk_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
> + struct jpeg_enc_param *p = &ctx->jpeg_param;
> + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> + int ret = 0;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_JPEG_RESTART_INTERVAL:
> + p->restart_interval = ctrl->val;
> + break;
> + case V4L2_CID_JPEG_COMPRESSION_QUALITY:
> + p->enc_quality = ctrl->val;
> + break;
> + case V4L2_CID_JPEG_ENABLE_EXIF:
> + p->enable_exif = ctrl->val;
> + break;
> + }
> +
> + v4l2_dbg(2, debug, &jpeg->v4l2_dev, "%s val = %d",
> + v4l2_ctrl_get_name(ctrl->id), ctrl->val);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops mtk_jpeg_ctrl_ops = {
> + .s_ctrl = vidioc_jpeg_s_ctrl,
> +};
> +
> +int mtk_jpeg_ctrls_setup(struct mtk_jpeg_ctx *ctx)
> +{
> + const struct v4l2_ctrl_ops *ops = &mtk_jpeg_ctrl_ops;
> + struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl;
> + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> +
> + v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT);
> +
> + if (jpeg->mode == MTK_JPEG_ENC) {
> + v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_RESTART_INTERVAL,
> + 0, 100, 1, 0);
> + v4l2_ctrl_new_std(handler, ops,
> + V4L2_CID_JPEG_COMPRESSION_QUALITY, 48, 100, 1,
> + 90);
> + v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_ENABLE_EXIF, 0,
> + 1, 1, 0);
> +
> + if (handler->error) {
> + v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> + return handler->error;
> + }
> + }
> +
> + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
>
> return 0;
> }
> @@ -118,23 +225,29 @@ static int mtk_jpeg_enum_fmt(struct mtk_jpeg_fmt *mtk_jpeg_formats, int n,
> static int mtk_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
> struct v4l2_fmtdesc *f)
> {
> + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> +
> return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
> + ctx->jpeg->mode == MTK_JPEG_ENC ?
> + MTK_JPEG_FMT_FLAG_ENC_CAPTURE :
> MTK_JPEG_FMT_FLAG_DEC_CAPTURE);
> }
>
> static int mtk_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
> struct v4l2_fmtdesc *f)
> {
> + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv);
> +
> return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f,
> + ctx->jpeg->mode == MTK_JPEG_ENC ?
> + MTK_JPEG_FMT_FLAG_ENC_OUTPUT :
> MTK_JPEG_FMT_FLAG_DEC_OUTPUT);
> }
>
> -static struct mtk_jpeg_q_data *mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx,
> - enum v4l2_buf_type type)
> +static struct mtk_jpeg_q_data *
> +mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx, enum v4l2_buf_type type)
> {
> - if (V4L2_TYPE_IS_OUTPUT(type))
> - return &ctx->out_q;
> - return &ctx->cap_q;
> + return V4L2_TYPE_IS_OUTPUT(type) ? &ctx->out_q : &ctx->cap_q;
> }
>
> static struct mtk_jpeg_fmt *mtk_jpeg_find_format(struct mtk_jpeg_ctx *ctx,
> @@ -143,9 +256,14 @@ static struct mtk_jpeg_fmt *mtk_jpeg_find_format(struct mtk_jpeg_ctx *ctx,
> {
> unsigned int k, fmt_flag;
>
> - fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ?
> - MTK_JPEG_FMT_FLAG_DEC_OUTPUT :
> - MTK_JPEG_FMT_FLAG_DEC_CAPTURE;
> + if (ctx->jpeg->mode == MTK_JPEG_ENC)
> + fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ?
> + MTK_JPEG_FMT_FLAG_ENC_OUTPUT :
> + MTK_JPEG_FMT_FLAG_ENC_CAPTURE;
> + else
> + fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ?
> + MTK_JPEG_FMT_FLAG_DEC_OUTPUT :
> + MTK_JPEG_FMT_FLAG_DEC_CAPTURE;
>
> for (k = 0; k < MTK_JPEG_NUM_FORMATS; k++) {
> struct mtk_jpeg_fmt *fmt = &mtk_jpeg_formats[k];
> @@ -202,7 +320,7 @@ static int mtk_jpeg_try_fmt_mplane(struct v4l2_format *f,
> {
> struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> - int i;
> + int i, align_w, align_h;
>
> memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
> pix_mp->field = V4L2_FIELD_NONE;
> @@ -216,36 +334,111 @@ static int mtk_jpeg_try_fmt_mplane(struct v4l2_format *f,
> pix_mp->pixelformat = fmt->fourcc;
>
> if (q_type == MTK_JPEG_FMT_TYPE_OUTPUT) {
> - struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[0];
> + if (jpeg->mode == MTK_JPEG_ENC) {
> + pix_mp->height = clamp(pix_mp->height,
> + MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT);
> + pix_mp->width = clamp(pix_mp->width,
> + MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH);
> + align_w = pix_mp->width;
> + align_h = pix_mp->height;
> + align_w = round_up(align_w, 2);
> + if (pix_mp->num_planes == 1U) {
> + align_w = align_w << 1;
> + mtk_jpeg_bound_align_image(&align_w,
> + MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH,
> + 5, &align_h,
> + MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT,
> + 3);
> + pix_mp->plane_fmt[0].bytesperline = align_w;
> + pix_mp->plane_fmt[0].sizeimage =
> + align_w * align_h;
> + } else if (pix_mp->num_planes == 2U) {
> + mtk_jpeg_bound_align_image(&align_w,
> + MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH,
> + 4, &align_h,
> + MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT,
> + 4);
> + pix_mp->plane_fmt[0].bytesperline = align_w;
> + pix_mp->plane_fmt[0].sizeimage =
> + align_w * align_h;
> + pix_mp->plane_fmt[1].bytesperline = align_w;
> + pix_mp->plane_fmt[1].sizeimage =
> + (align_w * align_h) / 2;
> + } else {
> + v4l2_err(&ctx->jpeg->v4l2_dev,
> + "Unsupport num planes = %d\n",
> + pix_mp->num_planes);
> + }
> + goto end;
> + } else {
> + struct v4l2_plane_pix_format *pfmt =
> + &pix_mp->plane_fmt[0];
> +
> + mtk_jpeg_bound_align_image(&pix_mp->width,
> + MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH, 0,
> + &pix_mp->height,
> + MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT, 0);
> +
> + pfmt->bytesperline = 0;
> + /* Source size must be aligned to 128 */
> + pfmt->sizeimage = mtk_jpeg_align(pfmt->sizeimage, 128);
> + if (pfmt->sizeimage == 0)
> + pfmt->sizeimage = MTK_JPEG_DEFAULT_SIZEIMAGE;
> +
> + goto end;
> + }
> + }
The differences between capture and output are too large IMHO. I would
recommend splitting this into different functions, one for the encoder,
one for the decoder.
>
> + /* type is MTK_JPEG_FMT_TYPE_CAPTURE */
> + if (jpeg->mode == MTK_JPEG_ENC) {
> mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> MTK_JPEG_MAX_WIDTH, 0,
> &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> MTK_JPEG_MAX_HEIGHT, 0);
>
> - memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> - pfmt->bytesperline = 0;
> - /* Source size must be aligned to 128 */
> - pfmt->sizeimage = mtk_jpeg_align(pfmt->sizeimage, 128);
> - if (pfmt->sizeimage == 0)
> - pfmt->sizeimage = MTK_JPEG_DEFAULT_SIZEIMAGE;
> - goto end;
> + if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
> + pix_mp->plane_fmt[0].bytesperline = 0;
> + pix_mp->plane_fmt[0].sizeimage =
> + mtk_jpeg_align(pix_mp->plane_fmt[0].sizeimage,
> + 128);
> + if (pix_mp->plane_fmt[0].sizeimage == 0)
> + pix_mp->plane_fmt[0].sizeimage =
> + MTK_JPEG_DEFAULT_SIZEIMAGE;
> + }
> + } else {
> + pix_mp->height = clamp(pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT);
> + pix_mp->width = clamp(pix_mp->width, MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH);
> + mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH, fmt->h_align,
> + &pix_mp->height,
> + MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT, fmt->v_align);
> +
> + for (i = 0; i < fmt->colplanes; i++) {
> + struct v4l2_plane_pix_format *pfmt =
> + &pix_mp->plane_fmt[i];
> + u32 stride = pix_mp->width * fmt->h_sample[i] / 4;
> + u32 h = pix_mp->height * fmt->v_sample[i] / 4;
> +
> + pfmt->bytesperline = stride;
> + pfmt->sizeimage = stride * h;
> + }
> }
>
> - /* type is MTK_JPEG_FMT_TYPE_CAPTURE */
> - mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH,
> - MTK_JPEG_MAX_WIDTH, fmt->h_align,
> - &pix_mp->height, MTK_JPEG_MIN_HEIGHT,
> - MTK_JPEG_MAX_HEIGHT, fmt->v_align);
> -
> for (i = 0; i < fmt->colplanes; i++) {
> - struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i];
> - u32 stride = pix_mp->width * fmt->h_sample[i] / 4;
> - u32 h = pix_mp->height * fmt->v_sample[i] / 4;
> -
> + struct v4l2_plane_pix_format *pfmt =
> + &pix_mp->plane_fmt[i];
> memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
> - pfmt->bytesperline = stride;
> - pfmt->sizeimage = stride * h;
> }
> end:
> v4l2_dbg(2, debug, &jpeg->v4l2_dev, "wxh:%ux%u\n",
> @@ -446,9 +639,9 @@ static int mtk_jpeg_subscribe_event(struct v4l2_fh *fh,
> switch (sub->type) {
> case V4L2_EVENT_SOURCE_CHANGE:
> return v4l2_src_change_event_subscribe(fh, sub);
> - default:
> - return -EINVAL;
> }
> +
> + return v4l2_ctrl_subscribe_event(fh, sub);
> }
This appears to be a bug fix. Please put this in a separate patch.
>
> static int mtk_jpeg_g_selection(struct file *file, void *priv,
> @@ -571,6 +764,13 @@ static int mtk_jpeg_queue_setup(struct vb2_queue *q,
> if (!q_data)
> return -EINVAL;
>
> + if (*num_planes) {
> + for (i = 0; i < *num_planes; i++)
> + if (sizes[i] < q_data->sizeimage[i])
> + return -EINVAL;
> + return 0;
> + }
> +
> *num_planes = q_data->fmt->colplanes;
> for (i = 0; i < q_data->fmt->colplanes; i++) {
> sizes[i] = q_data->sizeimage[i];
Also appears to be a bug fix.
> @@ -651,10 +851,92 @@ static void mtk_jpeg_set_queue_data(struct mtk_jpeg_ctx *ctx,
> param->dec_w, param->dec_h);
> }
>
> +static void mtk_jpeg_set_param(struct mtk_jpeg_ctx *ctx,
> + struct mtk_jpeg_enc_param *param)
> +{
> + struct mtk_jpeg_q_data *q_data_src = &ctx->out_q;
> + struct jpeg_enc_param *jpeg_params = &ctx->jpeg_param;
> + struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> + u32 width_even;
> + u32 is_420;
> + u32 padding_width;
> + u32 padding_height;
> +
> + switch (q_data_src->fmt->fourcc) {
> + case V4L2_PIX_FMT_YUYV:
> + param->enc_format = JPEG_YUV_FORMAT_YUYV;
> + break;
> + case V4L2_PIX_FMT_YVYU:
> + param->enc_format = JPEG_YUV_FORMAT_YVYU;
> + break;
> + case V4L2_PIX_FMT_NV12M:
> + param->enc_format = JPEG_YUV_FORMAT_NV12;
> + break;
> + case V4L2_PIX_FMT_NV21M:
> + param->enc_format = JPEG_YUV_FORMAT_NV12;
> + break;
> + default:
> + v4l2_err(&jpeg->v4l2_dev, "Unsupport fourcc =%d\n",
> + q_data_src->fmt->fourcc);
> + break;
> + }
> + param->enc_w = q_data_src->w;
> + param->enc_h = q_data_src->h;
> +
> + if (jpeg_params->enc_quality >= 97)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q97;
> + else if (jpeg_params->enc_quality >= 95)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q95;
> + else if (jpeg_params->enc_quality >= 92)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q92;
> + else if (jpeg_params->enc_quality >= 90)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q90;
> + else if (jpeg_params->enc_quality >= 87)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q87;
> + else if (jpeg_params->enc_quality >= 84)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q84;
> + else if (jpeg_params->enc_quality >= 80)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q80;
> + else if (jpeg_params->enc_quality >= 74)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q74;
> + else if (jpeg_params->enc_quality >= 64)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q64;
> + else if (jpeg_params->enc_quality >= 60)
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q60;
> + else
> + param->enc_quality = JPEG_ENCODE_QUALITY_Q48;
> +
> + param->enable_exif = jpeg_params->enable_exif;
> + param->restart_interval = jpeg_params->restart_interval;
> +
> + width_even = ((param->enc_w + 1) >> 1) << 1;
> + is_420 = (param->enc_format == JPEG_YUV_FORMAT_NV12 ||
> + param->enc_format == JPEG_YUV_FORMAT_NV12) ? 1 : 0;
> + padding_width = mtk_jpeg_align(param->enc_w, 16);
> + padding_height = mtk_jpeg_align(param->enc_h, is_420 ? 16 : 8);
> + if (!is_420)
> + width_even = width_even << 1;
> +
> + param->img_stride = mtk_jpeg_align(width_even, (is_420 ? 16 : 32));
> + param->mem_stride = mtk_jpeg_align(width_even, (is_420 ? 16 : 32));
> + param->total_encdu =
> + ((padding_width >> 4) * (padding_height >> (is_420 ? 4 : 3)) *
> + (is_420 ? 6 : 4)) - 1;
> +
> + v4l2_dbg(0, 2, &jpeg->v4l2_dev, "fmt %d, w,h %d,%d, enable_exif %d,",
> + "enc_quality %d, restart_interval %d,img_stride %d,",
> + "mem_stride %d,totalEncDu %d\n",
> + param->enc_format, param->enc_w, param->enc_h,
> + param->enable_exif, param->enc_quality,
> + param->restart_interval, param->img_stride,
> + param->mem_stride, param->total_encdu);
> +}
> +
> static void mtk_jpeg_buf_queue(struct vb2_buffer *vb)
> {
> struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> struct mtk_jpeg_dec_param *param;
> + struct mtk_jpeg_enc_param *enc_param;
> struct mtk_jpeg_dev *jpeg = ctx->jpeg;
> struct mtk_jpeg_src_buf *jpeg_src_buf;
> bool header_valid;
> @@ -666,29 +948,45 @@ static void mtk_jpeg_buf_queue(struct vb2_buffer *vb)
> goto end;
>
> jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb);
> - param = &jpeg_src_buf->dec_param;
> - memset(param, 0, sizeof(*param));
> -
> - if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> - v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos\n");
> - goto end;
> - }
> - header_valid = mtk_jpeg_parse(param, (u8 *)vb2_plane_vaddr(vb, 0),
> - vb2_get_plane_payload(vb, 0));
> - if (!header_valid) {
> - v4l2_err(&jpeg->v4l2_dev, "Header invalid.\n");
> - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> - return;
> - }
> -
> - if (ctx->state == MTK_JPEG_INIT) {
> - struct vb2_queue *dst_vq = v4l2_m2m_get_vq(
> - ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> + if (jpeg->mode == MTK_JPEG_ENC) {
> + enc_param = &jpeg_src_buf->enc_param;
> + memset(enc_param, 0, sizeof(*enc_param));
> + mtk_jpeg_set_param(ctx, enc_param);
> + if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos");
> + goto end;
> + }
> + if (ctx->state == MTK_JPEG_INIT)
> + ctx->state = MTK_JPEG_RUNNING;
> + } else {
> + param = &jpeg_src_buf->dec_param;
> + memset(param, 0, sizeof(*param));
> +
> + if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) {
> + v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos\n");
> + goto end;
> + }
> + header_valid = mtk_jpeg_parse(param,
> + (u8 *)vb2_plane_vaddr(vb, 0),
> + vb2_get_plane_payload(vb, 0));
> + if (!header_valid) {
> + v4l2_err(&jpeg->v4l2_dev, "Header invalid.\n");
> + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> + return;
> + }
>
> - mtk_jpeg_queue_src_chg_event(ctx);
> - mtk_jpeg_set_queue_data(ctx, param);
> - ctx->state = vb2_is_streaming(dst_vq) ?
> - MTK_JPEG_SOURCE_CHANGE : MTK_JPEG_RUNNING;
> + if (ctx->state == MTK_JPEG_INIT) {
> + struct vb2_queue *dst_vq;
> +
> + dst_vq = v4l2_m2m_get_vq
> + (ctx->fh.m2m_ctx,
> + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> + mtk_jpeg_queue_src_chg_event(ctx);
> + mtk_jpeg_set_queue_data(ctx, param);
> + ctx->state = vb2_is_streaming(dst_vq) ?
> + MTK_JPEG_SOURCE_CHANGE :
> + MTK_JPEG_RUNNING;
> + }
> }
> end:
> v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
> @@ -731,16 +1029,16 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q)
> * subsampling. Update capture queue when the stream is off.
> */
> if (ctx->state == MTK_JPEG_SOURCE_CHANGE &&
> - !V4L2_TYPE_IS_OUTPUT(q->type)) {
> + !V4L2_TYPE_IS_OUTPUT(q->type) &&
> + ctx->jpeg->mode == MTK_JPEG_DEC) {
> struct mtk_jpeg_src_buf *src_buf;
>
> vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> src_buf = mtk_jpeg_vb2_to_srcbuf(&vb->vb2_buf);
> mtk_jpeg_set_queue_data(ctx, &src_buf->dec_param);
> ctx->state = MTK_JPEG_RUNNING;
> - } else if (V4L2_TYPE_IS_OUTPUT(q->type)) {
> + } else if (V4L2_TYPE_IS_OUTPUT(q->type))
> ctx->state = MTK_JPEG_INIT;
> - }
>
> while ((vb = mtk_jpeg_buf_remove(ctx, q->type)))
> v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
> @@ -795,6 +1093,28 @@ static int mtk_jpeg_set_dec_dst(struct mtk_jpeg_ctx *ctx,
> return 0;
> }
>
> +static void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx,
> + struct vb2_buffer *dst_buf,
> + struct mtk_jpeg_enc_bs *bs)
> +{
> + bs->dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0) &
> + (~JPEG_ENC_DST_ADDR_OFFSET_MASK);
> + bs->dma_addr_offset = 0;
> + bs->dma_addr_offsetmask = bs->dma_addr & JPEG_ENC_DST_ADDR_OFFSET_MASK;
> + bs->size = mtk_jpeg_align(vb2_plane_size(dst_buf, 0), 128);
> +}
> +
> +static void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx,
> + struct vb2_buffer *src_buf,
> + struct mtk_jpeg_enc_fb *fb)
> +{
> + int i;
> +
> + for (i = 0; i < src_buf->num_planes; i++)
> + fb->fb_addr[i].dma_addr =
> + vb2_dma_contig_plane_dma_addr(src_buf, i);
> +}
> +
> static void mtk_jpeg_device_run(void *priv)
> {
> struct mtk_jpeg_ctx *ctx = priv;
> @@ -805,6 +1125,8 @@ static void mtk_jpeg_device_run(void *priv)
> struct mtk_jpeg_src_buf *jpeg_src_buf;
> struct mtk_jpeg_bs bs;
> struct mtk_jpeg_fb fb;
> + struct mtk_jpeg_enc_bs enc_bs;
> + struct mtk_jpeg_enc_fb enc_fb;
> int i;
>
> src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> @@ -815,30 +1137,45 @@ static void mtk_jpeg_device_run(void *priv)
> for (i = 0; i < dst_buf->vb2_buf.num_planes; i++)
> vb2_set_plane_payload(&dst_buf->vb2_buf, i, 0);
> buf_state = VB2_BUF_STATE_DONE;
> - goto dec_end;
> + goto device_run_end;
> }
>
> - if (mtk_jpeg_check_resolution_change(ctx, &jpeg_src_buf->dec_param)) {
> - mtk_jpeg_queue_src_chg_event(ctx);
> - ctx->state = MTK_JPEG_SOURCE_CHANGE;
> - v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> - return;
> - }
> + if (jpeg->mode == MTK_JPEG_ENC) {
> + mtk_jpeg_set_enc_dst(ctx, &dst_buf->vb2_buf, &enc_bs);
> + mtk_jpeg_set_enc_src(ctx, &src_buf->vb2_buf, &enc_fb);
> +
> + spin_lock_irqsave(&jpeg->hw_lock, flags);
> + mtk_jpeg_enc_reset(jpeg->reg_base);
> + mtk_jpeg_enc_set_config(jpeg->reg_base,
> + &jpeg_src_buf->enc_param, &enc_bs,
> + &enc_fb);
> +
> + mtk_jpeg_enc_start(jpeg->reg_base);
> + } else {
> + if (mtk_jpeg_check_resolution_change
> + (ctx, &jpeg_src_buf->dec_param)) {
> + mtk_jpeg_queue_src_chg_event(ctx);
> + ctx->state = MTK_JPEG_SOURCE_CHANGE;
> + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> + return;
> + }
>
> - mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs);
> - if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb))
> - goto dec_end;
> + mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs);
> + if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param,
> + &dst_buf->vb2_buf, &fb))
> + goto device_run_end;
>
> - spin_lock_irqsave(&jpeg->hw_lock, flags);
> - mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> - mtk_jpeg_dec_set_config(jpeg->dec_reg_base,
> - &jpeg_src_buf->dec_param, &bs, &fb);
> + spin_lock_irqsave(&jpeg->hw_lock, flags);
> + mtk_jpeg_dec_reset(jpeg->reg_base);
> + mtk_jpeg_dec_set_config(jpeg->reg_base,
> + &jpeg_src_buf->dec_param, &bs, &fb);
>
> - mtk_jpeg_dec_start(jpeg->dec_reg_base);
> + mtk_jpeg_dec_start(jpeg->reg_base);
> + }
> spin_unlock_irqrestore(&jpeg->hw_lock, flags);
> return;
>
> -dec_end:
> +device_run_end:
> v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> v4l2_m2m_buf_done(src_buf, buf_state);
> @@ -898,30 +1235,30 @@ static void mtk_jpeg_clk_on(struct mtk_jpeg_dev *jpeg)
> ret = mtk_smi_larb_get(jpeg->larb);
> if (ret)
> dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret);
> - clk_prepare_enable(jpeg->clk_jdec_smi);
> - clk_prepare_enable(jpeg->clk_jdec);
> + if (jpeg->mode == MTK_JPEG_DEC)
> + clk_prepare_enable(jpeg->clk_jpeg_smi);
> + clk_prepare_enable(jpeg->clk_jpeg);
> }
>
> static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg)
> {
> - clk_disable_unprepare(jpeg->clk_jdec);
> - clk_disable_unprepare(jpeg->clk_jdec_smi);
> + clk_disable_unprepare(jpeg->clk_jpeg);
> + if (jpeg->mode == MTK_JPEG_DEC)
> + clk_disable_unprepare(jpeg->clk_jpeg_smi);
> mtk_smi_larb_put(jpeg->larb);
> }
>
> -static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
> +static irqreturn_t mtk_jpeg_irq(int irq, void *priv)
> {
> struct mtk_jpeg_dev *jpeg = priv;
> struct mtk_jpeg_ctx *ctx;
> struct vb2_v4l2_buffer *src_buf, *dst_buf;
> struct mtk_jpeg_src_buf *jpeg_src_buf;
> enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR;
> - u32 dec_irq_ret;
> - u32 dec_ret;
> + u32 irq_ret;
> + u32 ret, result_size;
> int i;
>
> - dec_ret = mtk_jpeg_dec_get_int_status(jpeg->dec_reg_base);
> - dec_irq_ret = mtk_jpeg_dec_enum_result(dec_ret);
> ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
> if (!ctx) {
> v4l2_err(&jpeg->v4l2_dev, "Context is NULL\n");
> @@ -932,21 +1269,42 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
> dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf);
>
> - if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW)
> - mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> + if (jpeg->mode == MTK_JPEG_ENC) {
> + ret = mtk_jpeg_enc_get_int_status(jpeg->reg_base);
> + irq_ret = mtk_jpeg_enc_enum_result(jpeg->reg_base, ret,
> + &result_size);
>
> - if (dec_irq_ret != MTK_JPEG_DEC_RESULT_EOF_DONE) {
> - dev_err(jpeg->dev, "decode failed\n");
> - goto dec_end;
> - }
> + if (irq_ret >= MTK_JPEG_ENC_RESULT_STALL)
> + mtk_jpeg_enc_reset(jpeg->reg_base);
> +
> + if (irq_ret != MTK_JPEG_ENC_RESULT_DONE) {
> + dev_err(jpeg->dev, "encode failed\n");
> + goto irq_end;
> + }
>
> - for (i = 0; i < dst_buf->vb2_buf.num_planes; i++)
> - vb2_set_plane_payload(&dst_buf->vb2_buf, i,
> - jpeg_src_buf->dec_param.comp_size[i]);
> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
> + result_size);
> + } else {
> + ret = mtk_jpeg_dec_get_int_status(jpeg->reg_base);
> + irq_ret = mtk_jpeg_dec_enum_result(ret);
> +
> + if (irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW)
> + mtk_jpeg_dec_reset(jpeg->reg_base);
> +
> + if (irq_ret != MTK_JPEG_DEC_RESULT_EOF_DONE) {
> + dev_err(jpeg->dev, "decode failed\n");
> + goto irq_end;
> + }
> +
> + for (i = 0; i < dst_buf->vb2_buf.num_planes; i++)
> + vb2_set_plane_payload
> + (&dst_buf->vb2_buf, i,
> + jpeg_src_buf->dec_param.comp_size[i]);
> + }
>
> buf_state = VB2_BUF_STATE_DONE;
>
> -dec_end:
> +irq_end:
> v4l2_m2m_buf_done(src_buf, buf_state);
> v4l2_m2m_buf_done(dst_buf, buf_state);
> v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
> @@ -956,32 +1314,72 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv)
> static void mtk_jpeg_set_default_params(struct mtk_jpeg_ctx *ctx)
> {
> struct mtk_jpeg_q_data *q = &ctx->out_q;
> - int i;
> + int i, align_w, align_h;
> +
> + ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
>
> ctx->colorspace = V4L2_COLORSPACE_JPEG,
> ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
> ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
>
> - q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
> + if (ctx->jpeg->mode == MTK_JPEG_ENC) {
> + q->w = MTK_JPEG_MIN_WIDTH;
> + q->h = MTK_JPEG_MIN_HEIGHT;
> +
> + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV,
> MTK_JPEG_FMT_TYPE_OUTPUT);
> - q->w = MTK_JPEG_MIN_WIDTH;
> - q->h = MTK_JPEG_MIN_HEIGHT;
> - q->bytesperline[0] = 0;
> - q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE;
> +
> + align_w = q->w;
> + align_h = q->h;
> + align_w = round_up(align_w, 2);
> + align_w = align_w << 1;
> + v4l_bound_align_image(&align_w,
> + MTK_JPEG_MIN_WIDTH,
> + MTK_JPEG_MAX_WIDTH, 5,
> + &align_h,
> + MTK_JPEG_MIN_HEIGHT,
> + MTK_JPEG_MAX_HEIGHT, 3, 0);
> +
> + if (align_w < MTK_JPEG_MIN_WIDTH &&
> + (align_w + 32) <= MTK_JPEG_MAX_WIDTH)
> + align_w += 32;
> + if (align_h < MTK_JPEG_MIN_HEIGHT &&
> + (align_h + 8) <= MTK_JPEG_MAX_HEIGHT)
> + align_h += 8;
> +
> + q->sizeimage[0] = align_w * align_h;
> + q->bytesperline[0] = align_w;
> + } else {
> + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
> + MTK_JPEG_FMT_TYPE_OUTPUT);
> + q->w = MTK_JPEG_MIN_WIDTH;
> + q->h = MTK_JPEG_MIN_HEIGHT;
> + q->bytesperline[0] = 0;
> + q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE;
> + }
>
> q = &ctx->cap_q;
> - q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUV420M,
> + if (ctx->jpeg->mode == MTK_JPEG_ENC) {
> + q->w = MTK_JPEG_MIN_WIDTH;
> + q->h = MTK_JPEG_MIN_HEIGHT;
> + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
> + MTK_JPEG_FMT_TYPE_CAPTURE);
> + q->bytesperline[0] = 0;
> + q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE;
> + } else {
> + q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUV420M,
> MTK_JPEG_FMT_TYPE_CAPTURE);
> - q->w = MTK_JPEG_MIN_WIDTH;
> - q->h = MTK_JPEG_MIN_HEIGHT;
> + q->w = MTK_JPEG_MIN_WIDTH;
> + q->h = MTK_JPEG_MIN_HEIGHT;
>
> - for (i = 0; i < q->fmt->colplanes; i++) {
> - u32 stride = q->w * q->fmt->h_sample[i] / 4;
> - u32 h = q->h * q->fmt->v_sample[i] / 4;
> + for (i = 0; i < q->fmt->colplanes; i++) {
> + u32 stride = q->w * q->fmt->h_sample[i] / 4;
> + u32 h = q->h * q->fmt->v_sample[i] / 4;
>
> - q->bytesperline[i] = stride;
> - q->sizeimage[i] = stride * h;
> + q->bytesperline[i] = stride;
> + q->sizeimage[i] = stride * h;
> + }
> }
> }
This is another function were there appears to be little overlap between
encoder and decoder. A candidate to split up?
>
> @@ -1013,6 +1411,13 @@ static int mtk_jpeg_open(struct file *file)
> goto error;
> }
>
> + ret = mtk_jpeg_ctrls_setup(ctx);
> + if (ret) {
> + v4l2_err(&jpeg->v4l2_dev, "Failed to setup controls() (%d)\n",
> + ret);
> + goto error;
> + }
> +
> mtk_jpeg_set_default_params(ctx);
> mutex_unlock(&jpeg->lock);
> return 0;
> @@ -1033,6 +1438,7 @@ static int mtk_jpeg_release(struct file *file)
>
> mutex_lock(&jpeg->lock);
> v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> + v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> v4l2_fh_del(&ctx->fh);
> v4l2_fh_exit(&ctx->fh);
> kfree(ctx);
> @@ -1057,6 +1463,7 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
> node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0);
> if (!node)
> return -EINVAL;
> +
> pdev = of_find_device_by_node(node);
> if (WARN_ON(!pdev)) {
> of_node_put(node);
> @@ -1066,19 +1473,24 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
>
> jpeg->larb = &pdev->dev;
>
> - jpeg->clk_jdec = devm_clk_get(jpeg->dev, "jpgdec");
> - if (IS_ERR(jpeg->clk_jdec))
> - return PTR_ERR(jpeg->clk_jdec);
> + if (jpeg->mode == MTK_JPEG_ENC) {
> + jpeg->clk_jpeg = devm_clk_get(jpeg->dev, "jpgenc");
> + return PTR_ERR_OR_ZERO(jpeg->clk_jpeg);
> + }
> +
> + jpeg->clk_jpeg = devm_clk_get(jpeg->dev, "jpgdec");
> + if (IS_ERR(jpeg->clk_jpeg))
> + return PTR_ERR(jpeg->clk_jpeg);
>
> - jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
> - return PTR_ERR_OR_ZERO(jpeg->clk_jdec_smi);
> + jpeg->clk_jpeg_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
> + return PTR_ERR_OR_ZERO(jpeg->clk_jpeg_smi);
> }
>
> static int mtk_jpeg_probe(struct platform_device *pdev)
> {
> struct mtk_jpeg_dev *jpeg;
> struct resource *res;
> - int dec_irq;
> + int jpeg_irq;
> int ret;
>
> jpeg = devm_kzalloc(&pdev->dev, sizeof(*jpeg), GFP_KERNEL);
> @@ -1088,28 +1500,26 @@ static int mtk_jpeg_probe(struct platform_device *pdev)
> mutex_init(&jpeg->lock);
> spin_lock_init(&jpeg->hw_lock);
> jpeg->dev = &pdev->dev;
> + jpeg->mode = (enum mtk_jpeg_mode)of_device_get_match_data(jpeg->dev);
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - jpeg->dec_reg_base = devm_ioremap_resource(&pdev->dev, res);
> - if (IS_ERR(jpeg->dec_reg_base)) {
> - ret = PTR_ERR(jpeg->dec_reg_base);
> + jpeg->reg_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(jpeg->reg_base)) {
> + ret = PTR_ERR(jpeg->reg_base);
> return ret;
> }
>
> - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> - dec_irq = platform_get_irq(pdev, 0);
> - if (!res || dec_irq < 0) {
> - dev_err(&pdev->dev, "Failed to get dec_irq %d.\n", dec_irq);
> - ret = -EINVAL;
> - return ret;
> + jpeg_irq = platform_get_irq(pdev, 0);
> + if (jpeg_irq < 0) {
> + dev_err(&pdev->dev, "Failed to get jpeg_irq %d.\n", jpeg_irq);
> + return jpeg_irq;
> }
>
> - ret = devm_request_irq(&pdev->dev, dec_irq, mtk_jpeg_dec_irq, 0,
> + ret = devm_request_irq(&pdev->dev, jpeg_irq, mtk_jpeg_irq, 0,
> pdev->name, jpeg);
> if (ret) {
> - dev_err(&pdev->dev, "Failed to request dec_irq %d (%d)\n",
> - dec_irq, ret);
> - ret = -EINVAL;
> + dev_err(&pdev->dev, "Failed to request jpeg_irq %d (%d)\n",
> + jpeg_irq, ret);
> goto err_req_irq;
> }
>
> @@ -1133,33 +1543,35 @@ static int mtk_jpeg_probe(struct platform_device *pdev)
> goto err_m2m_init;
> }
>
> - jpeg->dec_vdev = video_device_alloc();
> - if (!jpeg->dec_vdev) {
> + jpeg->vfd_jpeg = video_device_alloc();
> + if (!jpeg->vfd_jpeg) {
> ret = -ENOMEM;
> - goto err_dec_vdev_alloc;
> + goto err_vfd_jpeg_alloc;
> }
> - snprintf(jpeg->dec_vdev->name, sizeof(jpeg->dec_vdev->name),
> - "%s-dec", MTK_JPEG_NAME);
> - jpeg->dec_vdev->fops = &mtk_jpeg_fops;
> - jpeg->dec_vdev->ioctl_ops = &mtk_jpeg_ioctl_ops;
> - jpeg->dec_vdev->minor = -1;
> - jpeg->dec_vdev->release = video_device_release;
> - jpeg->dec_vdev->lock = &jpeg->lock;
> - jpeg->dec_vdev->v4l2_dev = &jpeg->v4l2_dev;
> - jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M;
> - jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING |
> + snprintf(jpeg->vfd_jpeg->name, sizeof(jpeg->vfd_jpeg->name),
> + "%s-%s", MTK_JPEG_NAME,
> + jpeg->mode == MTK_JPEG_ENC ? "enc" : "dec");
> + jpeg->vfd_jpeg->fops = &mtk_jpeg_fops;
> + jpeg->vfd_jpeg->ioctl_ops = &mtk_jpeg_ioctl_ops;
> + jpeg->vfd_jpeg->minor = -1;
> + jpeg->vfd_jpeg->release = video_device_release;
> + jpeg->vfd_jpeg->lock = &jpeg->lock;
> + jpeg->vfd_jpeg->v4l2_dev = &jpeg->v4l2_dev;
> + jpeg->vfd_jpeg->vfl_dir = VFL_DIR_M2M;
> + jpeg->vfd_jpeg->device_caps = V4L2_CAP_STREAMING |
> V4L2_CAP_VIDEO_M2M_MPLANE;
>
> - ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_GRABBER, 3);
> + ret = video_register_device(jpeg->vfd_jpeg, VFL_TYPE_GRABBER, -1);
> if (ret) {
> v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
> - goto err_dec_vdev_register;
> + goto err_vfd_jpeg_register;
> }
>
> - video_set_drvdata(jpeg->dec_vdev, jpeg);
> + video_set_drvdata(jpeg->vfd_jpeg, jpeg);
> v4l2_info(&jpeg->v4l2_dev,
> - "decoder device registered as /dev/video%d (%d,%d)\n",
> - jpeg->dec_vdev->num, VIDEO_MAJOR, jpeg->dec_vdev->minor);
> + "jpeg device %d registered as /dev/video%d (%d,%d)\n",
> + jpeg->mode, jpeg->vfd_jpeg->num, VIDEO_MAJOR,
> + jpeg->vfd_jpeg->minor);
>
> platform_set_drvdata(pdev, jpeg);
>
> @@ -1167,10 +1579,10 @@ static int mtk_jpeg_probe(struct platform_device *pdev)
>
> return 0;
>
> -err_dec_vdev_register:
> - video_device_release(jpeg->dec_vdev);
> +err_vfd_jpeg_register:
> + video_device_release(jpeg->vfd_jpeg);
>
> -err_dec_vdev_alloc:
> +err_vfd_jpeg_alloc:
> v4l2_m2m_release(jpeg->m2m_dev);
>
> err_m2m_init:
> @@ -1190,8 +1602,8 @@ static int mtk_jpeg_remove(struct platform_device *pdev)
> struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev);
>
> pm_runtime_disable(&pdev->dev);
> - video_unregister_device(jpeg->dec_vdev);
> - video_device_release(jpeg->dec_vdev);
> + video_unregister_device(jpeg->vfd_jpeg);
> + video_device_release(jpeg->vfd_jpeg);
> v4l2_m2m_release(jpeg->m2m_dev);
> v4l2_device_unregister(&jpeg->v4l2_dev);
>
> @@ -1202,7 +1614,11 @@ static __maybe_unused int mtk_jpeg_pm_suspend(struct device *dev)
> {
> struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
>
> - mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> + if (jpeg->mode == MTK_JPEG_ENC)
> + mtk_jpeg_enc_reset(jpeg->reg_base);
> + else
> + mtk_jpeg_dec_reset(jpeg->reg_base);
> +
> mtk_jpeg_clk_off(jpeg);
>
> return 0;
> @@ -1213,7 +1629,10 @@ static __maybe_unused int mtk_jpeg_pm_resume(struct device *dev)
> struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev);
>
> mtk_jpeg_clk_on(jpeg);
> - mtk_jpeg_dec_reset(jpeg->dec_reg_base);
> + if (jpeg->mode == MTK_JPEG_ENC)
> + mtk_jpeg_enc_reset(jpeg->reg_base);
> + else
> + mtk_jpeg_dec_reset(jpeg->reg_base);
>
> return 0;
> }
> @@ -1249,11 +1668,15 @@ static const struct dev_pm_ops mtk_jpeg_pm_ops = {
> static const struct of_device_id mtk_jpeg_match[] = {
> {
> .compatible = "mediatek,mt8173-jpgdec",
> - .data = NULL,
> + .data = (void *)MTK_JPEG_DEC,
> },
> {
> .compatible = "mediatek,mt2701-jpgdec",
> - .data = NULL,
> + .data = (void *)MTK_JPEG_DEC,
> + },
> + {
> + .compatible = "mediatek,mtk-jpgenc",
> + .data = (void *)MTK_JPEG_ENC,
> },
> {},
> };
The g/s_selection ioctls are unchanged: that can't be right.
The decoder supports composing, but that's not valid for an encoder:
I expect cropping support for an encoder.
Regards,
Hans
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* RE: [v4,1/3] soc: fsl: handle RCPM errata A-008646 on SoC LS1021A
From: Biwen Li @ 2019-09-26 9:58 UTC (permalink / raw)
To: Biwen Li, Leo Li, shawnguo@kernel.org, robh+dt@kernel.org,
mark.rutland@arm.com, Ran Wang
Cc: devicetree@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190926024118.15931-1-biwen.li@nxp.com>
Hi all,
the linux patch depended by RCPM driver,FlexTimer driver and FlexTimer dts, need apply these patches as follows:
1. RCPM driver:
https://patchwork.kernel.org/series/162731/mbox/ (https://patchwork.kernel.org/patch/11105279/)
2. FlexTimer dts:
https://lore.kernel.org/patchwork/series/405653/mbox/ (https://lore.kernel.org/patchwork/patch/1112493/)
3. FlexTimer driver:
https://patchwork.ozlabs.org/series/124718/mbox/ (https://patchwork.ozlabs.org/patch/1145999/)
https://patchwork.ozlabs.org/series/126942/mbox/ (https://patchwork.ozlabs.org/patch/1152085/)
4. Adjust drivers/soc/fsl/Makefile:
remove the line 'obj-y += ftm_alarm.o' in drivers/soc/fsl/Makefile to resolve a compilation error
> Description:
> - Reading configuration register RCPM_IPPDEXPCR1
> always return zero
>
> Workaround:
> - Save register RCPM_IPPDEXPCR1's value to
> register SCFG_SPARECR8.(uboot's psci also
> need reading value from the register SCFG_SPARECR8
> to set register RCPM_IPPDEXPCR1)
>
> Impact:
> - FlexTimer module will cannot wakeup system in
> deep sleep on SoC LS1021A
>
> Signed-off-by: Biwen Li <biwen.li@nxp.com>
> ---
> Change in v4:
> - rename property name
> fsl,ippdexpcr-alt-addr -> fsl,ippdexpcr1-alt-addr
>
> Change in v3:
> - update commit message
> - rename property name
> fsl,rcpm-scfg -> fsl,ippdexpcr-alt-addr
>
> Change in v2:
> - fix stype problems
>
> drivers/soc/fsl/rcpm.c | 33 +++++++++++++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/drivers/soc/fsl/rcpm.c b/drivers/soc/fsl/rcpm.c index
> 82c0ad5e663e..9a29c482fc2e 100644
> --- a/drivers/soc/fsl/rcpm.c
> +++ b/drivers/soc/fsl/rcpm.c
> @@ -13,6 +13,8 @@
> #include <linux/slab.h>
> #include <linux/suspend.h>
> #include <linux/kernel.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
>
> #define RCPM_WAKEUP_CELL_MAX_SIZE 7
>
> @@ -29,6 +31,9 @@ static int rcpm_pm_prepare(struct device *dev)
> struct rcpm *rcpm;
> u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1], tmp;
> int i, ret, idx;
> + struct regmap *scfg_addr_regmap = NULL;
> + u32 reg_offset[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
> + u32 reg_value = 0;
>
> rcpm = dev_get_drvdata(dev);
> if (!rcpm)
> @@ -63,6 +68,34 @@ static int rcpm_pm_prepare(struct device *dev)
> tmp |= value[i + 1];
> iowrite32be(tmp, rcpm->ippdexpcr_base + i * 4);
> }
> + /* Workaround of errata A-008646 on SoC LS1021A:
> + * There is a bug of register ippdexpcr1.
> + * Reading configuration register RCPM_IPPDEXPCR1
> + * always return zero. So save ippdexpcr1's value
> + * to register SCFG_SPARECR8.And the value of
> + * ippdexpcr1 will be read from SCFG_SPARECR8.
> + */
> + scfg_addr_regmap =
> syscon_regmap_lookup_by_phandle(np,
> +
> "fsl,ippdexpcr1-alt-addr");
> + if (scfg_addr_regmap && (1 == i)) {
> + if (of_property_read_u32_array(dev->of_node,
> + "fsl,ippdexpcr1-alt-addr",
> + reg_offset,
> + 1 + sizeof(u64)/sizeof(u32))) {
> + scfg_addr_regmap = NULL;
> + continue;
> + }
> + /* Read value from register SCFG_SPARECR8 */
> + regmap_read(scfg_addr_regmap,
> + (u32)(((u64)(reg_offset[1] << (sizeof(u32) *
> 8) |
> + reg_offset[2])) & 0xffffffff),
> + ®_value);
> + /* Write value to register SCFG_SPARECR8 */
> + regmap_write(scfg_addr_regmap,
> + (u32)(((u64)(reg_offset[1] << (sizeof(u32) *
> 8) |
> + reg_offset[2])) & 0xffffffff),
> + tmp | reg_value);
> + }
> }
> }
> } while (ws = wakeup_source_get_next(ws));
> --
> 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
* Re: [PATCH] firmware: imx: Skip return value check for some special SCU firmware APIs
From: Marco Felsch @ 2019-09-26 10:05 UTC (permalink / raw)
To: Anson Huang
Cc: Aisheng Dong, shawnguo@kernel.org, s.hauer@pengutronix.de,
linux-kernel@vger.kernel.org, dl-linux-imx, kernel@pengutronix.de,
Leonard Crestez, festevam@gmail.com,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <DB3PR0402MB391683202692BEAE4D2CD9C1F5860@DB3PR0402MB3916.eurprd04.prod.outlook.com>
Hi Anson,
On 19-09-26 08:03, Anson Huang wrote:
> Hi, Marco
>
> > On 19-09-25 18:07, Anson Huang wrote:
> > > The SCU firmware does NOT always have return value stored in message
> > > header's function element even the API has response data, those
> > > special APIs are defined as void function in SCU firmware, so they
> > > should be treated as return success always.
> > >
> > > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > > ---
> > > - This patch is based on the patch of
> > > https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatc
> > >
> > hwork.kernel.org%2Fpatch%2F11129553%2F&data=02%7C01%7Canson.
> > huang%
> > >
> > 40nxp.com%7C1f4108cc25eb4618f43c08d742576fa3%7C686ea1d3bc2b4c6fa
> > 92cd99
> > >
> > c5c301635%7C0%7C0%7C637050815608963707&sdata=BZBg4cOR2rP%2
> > BRBNn15i
> > > Qq3%2FXBYwhuCLkgYzFRbfEgVU%3D&reserved=0
> > > ---
> > > drivers/firmware/imx/imx-scu.c | 34
> > > ++++++++++++++++++++++++++++++++--
> > > 1 file changed, 32 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/firmware/imx/imx-scu.c
> > > b/drivers/firmware/imx/imx-scu.c index 869be7a..ced5b12 100644
> > > --- a/drivers/firmware/imx/imx-scu.c
> > > +++ b/drivers/firmware/imx/imx-scu.c
> > > @@ -78,6 +78,11 @@ static int imx_sc_linux_errmap[IMX_SC_ERR_LAST] =
> > {
> > > -EIO, /* IMX_SC_ERR_FAIL */
> > > };
> > >
> > > +static const struct imx_sc_rpc_msg whitelist[] = {
> > > + { .svc = IMX_SC_RPC_SVC_MISC, .func =
> > IMX_SC_MISC_FUNC_UNIQUE_ID },
> > > + { .svc = IMX_SC_RPC_SVC_MISC, .func =
> > > +IMX_SC_MISC_FUNC_GET_BUTTON_STATUS }, };
> >
> > Is this going to be extended in the near future? I see some upcoming
> > problems here if someone uses a different scu-fw<->kernel combination as
> > nxp would suggest.
>
> Could be, but I checked the current APIs, ONLY these 2 will be used in Linux kernel, so
> I ONLY add these 2 APIs for now.
Okay.
> However, after rethink, maybe we should add another imx_sc_rpc API for those special
> APIs? To avoid checking it for all the APIs called which may impact some performance.
> Still under discussion, if you have better idea, please advise, thanks!
Adding a special api shouldn't be the right fix. Imagine if someone (not
a nxp-developer) wants to add a new driver. How could he be expected to
know which api he should use. The better abbroach would be to fix the
scu-fw instead of adding quirks..
Regards,
Marco
>
> Anson
_______________________________________________
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] soc: xilinx: Set CAP_UNUSABLE requirement for versal while powering down domain
From: Michal Simek @ 2019-09-26 10:09 UTC (permalink / raw)
To: Jolly Shah, matthias.bgg, andy.gross, shawnguo, geert+renesas,
bjorn.andersson, sean.wang, m.szyprowski, michal.simek
Cc: Tejas Patel, rajanv, linux-kernel, linux-arm-kernel
In-Reply-To: <1566851444-22842-1-git-send-email-jolly.shah@xilinx.com>
On 26. 08. 19 22:30, Jolly Shah wrote:
> From: Tejas Patel <tejas.patel@xilinx.com>
>
> For "0" requirement which is used to inform firmware that device is
> not required currently by master, Versal PLM (Platform Loader and
> Manager) which runs on Platform Management Controller and is responsible
> platform management of devices that disables clock, power it down
> and reset the device. genpd_power_off() is being called during runtime
> suspend also. So, if any device goes to runtime suspend state during
> resumes it needs to be re-initialized again. It is possible that
> drivers do not reinitialize device upon resume from runtime suspend
> every time ans so dont want it to be powered down or get reset
> during runtime suspend.
>
> In Versal PLM new PM_CAP_UNUSABLE capability is added, which disables
> clock only and avoids power down and reset during runtime suspend. Power
> and reset will be gated with core suspend.So, this patch sets
> CAPABILITY_UNUSABLE requirement during gpd_power_off()
> if platform is other than zynqmp.
>
> Signed-off-by: Tejas Patel <tejas.patel@xilinx.com>
> Signed-off-by: Jolly Shah <jolly.shah@xilinx.com>
> ---
Where is the version log?
> drivers/soc/xilinx/zynqmp_pm_domains.c | 10 ++++++++--
> include/linux/firmware/xlnx-zynqmp.h | 3 ++-
> 2 files changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c
> index 600f57c..23d90cb 100644
> --- a/drivers/soc/xilinx/zynqmp_pm_domains.c
> +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c
> @@ -2,7 +2,7 @@
> /*
> * ZynqMP Generic PM domain support
> *
> - * Copyright (C) 2015-2018 Xilinx, Inc.
> + * Copyright (C) 2015-2019 Xilinx, Inc.
> *
> * Davorin Mista <davorin.mista@aggios.com>
> * Jolly Shah <jollys@xilinx.com>
> @@ -25,6 +25,8 @@
>
> static const struct zynqmp_eemi_ops *eemi_ops;
>
> +static int min_capability;
> +
> /**
> * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain
> * @gpd: Generic power domain
> @@ -106,7 +108,7 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
> int ret;
> struct pm_domain_data *pdd, *tmp;
> struct zynqmp_pm_domain *pd;
> - u32 capabilities = 0;
> + u32 capabilities = min_capability;
> bool may_wakeup;
>
> if (!eemi_ops->set_requirement)
> @@ -283,6 +285,10 @@ static int zynqmp_gpd_probe(struct platform_device *pdev)
> if (!domains)
> return -ENOMEM;
>
> + if (!of_device_is_compatible(dev->parent->of_node,
> + "xlnx,zynqmp-firmware"))
> + min_capability = ZYNQMP_PM_CAPABILITY_UNUSABLE;
I have not a problem with this patch but versal firmware is not wired in
firmware yet that's why this code shouldn't be called at all.
Can you please wire it?
Thanks,
Michal
_______________________________________________
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] arm64: zynqmp: Add dr_mode property to usb node
From: Michal Simek @ 2019-09-26 10:11 UTC (permalink / raw)
To: Anurag Kumar Vulisha, robh+dt, mark.rutland, michal.simek,
ulf.hansson, sudeep.holla, jan.kiszka
Cc: v.anuragkumar, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <1568128642-4782-1-git-send-email-anurag.kumar.vulisha@xilinx.com>
On 10. 09. 19 17:17, Anurag Kumar Vulisha wrote:
> This patch adds dr_mode property to the usb node for
> zynqmp boards.
>
> Signed-off-by: Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>
> ---
> arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm015-dc1.dts | 1 +
> arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm016-dc2.dts | 1 +
> arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts | 2 ++
> arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts | 1 +
> arch/arm64/boot/dts/xilinx/zynqmp-zcu104-revA.dts | 1 +
> arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts | 1 +
> arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts | 1 +
> 7 files changed, 8 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm015-dc1.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm015-dc1.dts
> index 9a3e39d..386f701 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm015-dc1.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm015-dc1.dts
> @@ -128,4 +128,5 @@
> /* ULPI SMSC USB3320 */
> &usb0 {
> status = "okay";
> + dr_mode = "host";
> };
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm016-dc2.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm016-dc2.dts
> index 2421ec7..c6c8e2c 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm016-dc2.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zc1751-xm016-dc2.dts
> @@ -158,6 +158,7 @@
> /* ULPI SMSC USB3320 */
> &usb1 {
> status = "okay";
> + dr_mode = "host";
> };
>
> &uart0 {
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts
> index e5699d0..c64c7b8 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu100-revC.dts
> @@ -274,11 +274,13 @@
> /* ULPI SMSC USB3320 */
> &usb0 {
> status = "okay";
> + dr_mode = "peripheral";
> };
>
> /* ULPI SMSC USB3320 */
> &usb1 {
> status = "okay";
> + dr_mode = "host";
> };
>
> &watchdog0 {
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts
> index 2a3b665..011f138 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-revA.dts
> @@ -540,6 +540,7 @@
> /* ULPI SMSC USB3320 */
> &usb0 {
> status = "okay";
> + dr_mode = "host";
> };
>
> &watchdog0 {
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu104-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu104-revA.dts
> index 8f45614..7e372b1 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu104-revA.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu104-revA.dts
> @@ -189,6 +189,7 @@
> /* ULPI SMSC USB3320 */
> &usb0 {
> status = "okay";
> + dr_mode = "host";
> };
>
> &watchdog0 {
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
> index 93ce7eb..c4aac7a 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
> @@ -514,6 +514,7 @@
> /* ULPI SMSC USB3320 */
> &usb0 {
> status = "okay";
> + dr_mode = "host";
> };
>
> &watchdog0 {
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts
> index 8bb0001..4909533 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu111-revA.dts
> @@ -440,4 +440,5 @@
> /* ULPI SMSC USB3320 */
> &usb0 {
> status = "okay";
> + dr_mode = "host";
> };
>
Applied.
M
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* RE: [RFC PATCH 00/18] crypto: wireguard using the existing crypto API
From: Pascal Van Leeuwen @ 2019-09-26 10:19 UTC (permalink / raw)
To: Jason A. Donenfeld, Ard Biesheuvel
Cc: Catalin Marinas, Herbert Xu, Arnd Bergmann, Eric Biggers, Greg KH,
Samuel Neves, Will Deacon, Linux Crypto Mailing List,
Andy Lutomirski, Marc Zyngier, Dan Carpenter, Linus Torvalds,
David Miller, linux-arm-kernel
In-Reply-To: <CAHmME9oDhnv7aX77oEERof0TGihk4mDe9B_A3AntaTTVsg9aoA@mail.gmail.com>
> There’s also a degree of practicality: right now there is zero ChaPoly
> async acceleration hardware anywhere that would fit into the crypto
> API.
>
Actually, that assumption is factually wrong. I don't know if anything
is *publicly* available, but I can assure you the silicon is running in
labs already. And something will be publicly available early next year
at the latest. Which could nicely coincide with having Wireguard support
in the kernel (which I would also like to see happen BTW) ...
> At some point, it might come to exist and have incredible
> performance, and then we’ll both feel very motivated to make this work
> for WireGuard. But it might also not come to be (AES seems to have won
> over most of the industry), in which case, why hassle?
>
Not "at some point". It will. Very soon. Maybe not in consumer or server
CPUs, but definitely in the embedded (networking) space.
And it *will* be much faster than the embedded CPU next to it, so it will
be worth using it for something like bulk packet encryption.
Regards,
Pascal van Leeuwen
Silicon IP Architect, Multi-Protocol Engines @ Verimatrix
www.insidesecure.com
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] power: reset: at91-poweroff: lookup for proper PMC DT node
From: Claudiu Beznea @ 2019-09-26 10:25 UTC (permalink / raw)
To: nicolas.ferre, sre, alexandre.belloni, ludovic.desroches
Cc: Claudiu Beznea, linux-kernel, linux-arm-kernel, linux-pm
Driver has been enabled also for SAM9X60. At the moment the patch which did
this has been sent to mainline the PMC for SAM9X60 wasn't integrated.
SAM9X60 has a new PMC compatible (see commit 01e2113de9a5
("clk: at91: add sam9x60 pmc driver")). Do to this we have to
look for proper PMC compatible here, in SHDWC driver.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/power/reset/at91-sama5d2_shdwc.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c
index e341cc5c0ea6..1c18f465a245 100644
--- a/drivers/power/reset/at91-sama5d2_shdwc.c
+++ b/drivers/power/reset/at91-sama5d2_shdwc.c
@@ -269,6 +269,12 @@ static const struct of_device_id at91_shdwc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, at91_shdwc_of_match);
+static const struct of_device_id at91_pmc_ids[] = {
+ { .compatible = "atmel,sama5d2-pmc" },
+ { .compatible = "microchip,sam9x60-pmc" },
+ { /* Sentinel. */ }
+};
+
static int __init at91_shdwc_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -313,7 +319,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
at91_shdwc_dt_configure(pdev);
- np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-pmc");
+ np = of_find_matching_node(NULL, at91_pmc_ids);
if (!np) {
ret = -ENODEV;
goto clk_disable;
--
2.7.4
_______________________________________________
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 v9 3/3] mm: fix double page fault on arm64 if PTE_AF is cleared
From: Kirill A. Shutemov @ 2019-09-26 10:25 UTC (permalink / raw)
To: Jia He
Cc: Mark Rutland, Catalin Marinas, linux-mm, Punit Agrawal,
Will Deacon, Alex Van Brunt, Marc Zyngier, Anshuman Khandual,
Matthew Wilcox, Kaly Xin, hejianet, Ralph Campbell,
Suzuki Poulose, Jérôme Glisse, Thomas Gleixner, nd,
linux-arm-kernel, linux-kernel, James Morse, Andrew Morton,
Robin Murphy, Kirill A. Shutemov
In-Reply-To: <20190925025922.176362-4-justin.he@arm.com>
On Wed, Sep 25, 2019 at 10:59:22AM +0800, Jia He wrote:
> When we tested pmdk unit test [1] vmmalloc_fork TEST1 in arm64 guest, there
> will be a double page fault in __copy_from_user_inatomic of cow_user_page.
>
> Below call trace is from arm64 do_page_fault for debugging purpose
> [ 110.016195] Call trace:
> [ 110.016826] do_page_fault+0x5a4/0x690
> [ 110.017812] do_mem_abort+0x50/0xb0
> [ 110.018726] el1_da+0x20/0xc4
> [ 110.019492] __arch_copy_from_user+0x180/0x280
> [ 110.020646] do_wp_page+0xb0/0x860
> [ 110.021517] __handle_mm_fault+0x994/0x1338
> [ 110.022606] handle_mm_fault+0xe8/0x180
> [ 110.023584] do_page_fault+0x240/0x690
> [ 110.024535] do_mem_abort+0x50/0xb0
> [ 110.025423] el0_da+0x20/0x24
>
> The pte info before __copy_from_user_inatomic is (PTE_AF is cleared):
> [ffff9b007000] pgd=000000023d4f8003, pud=000000023da9b003, pmd=000000023d4b3003, pte=360000298607bd3
>
> As told by Catalin: "On arm64 without hardware Access Flag, copying from
> user will fail because the pte is old and cannot be marked young. So we
> always end up with zeroed page after fork() + CoW for pfn mappings. we
> don't always have a hardware-managed access flag on arm64."
>
> This patch fix it by calling pte_mkyoung. Also, the parameter is
> changed because vmf should be passed to cow_user_page()
>
> Add a WARN_ON_ONCE when __copy_from_user_inatomic() returns error
> in case there can be some obscure use-case.(by Kirill)
>
> [1] https://github.com/pmem/pmdk/tree/master/src/test/vmmalloc_fork
>
> Signed-off-by: Jia He <justin.he@arm.com>
> Reported-by: Yibo Cai <Yibo.Cai@arm.com>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
--
Kirill A. Shutemov
_______________________________________________
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 01/11] PCI: designware-ep: Add multiple PFs support for DWC
From: Andrew Murray @ 2019-09-26 10:29 UTC (permalink / raw)
To: Xiaowei Bao, jingoohan1@gmail.com, gustavo.pimentel@synopsys.com
Cc: mark.rutland@arm.com, Roy Zang, lorenzo.pieralisi@arm.com,
arnd@arndb.de, devicetree@vger.kernel.org,
linux-pci@vger.kernel.org, Z.q. Hou,
linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org,
kishon@ti.com, M.h. Lian, robh+dt@kernel.org,
linux-arm-kernel@lists.infradead.org, gregkh@linuxfoundation.org,
Leo Li, shawnguo@kernel.org, Mingkai Hu
In-Reply-To: <AM5PR04MB3299C197920160E53FEEDD7AF5B90@AM5PR04MB3299.eurprd04.prod.outlook.com>
On Tue, Sep 03, 2019 at 03:43:15AM +0000, Xiaowei Bao wrote:
>
>
> > -----Original Message-----
> > From: Andrew Murray <andrew.murray@arm.com>
> > Sent: 2019年9月3日 0:26
> > To: Xiaowei Bao <xiaowei.bao@nxp.com>
> > Cc: robh+dt@kernel.org; mark.rutland@arm.com; shawnguo@kernel.org; Leo
> > Li <leoyang.li@nxp.com>; kishon@ti.com; lorenzo.pieralisi@arm.com; M.h.
> > Lian <minghuan.lian@nxp.com>; Mingkai Hu <mingkai.hu@nxp.com>; Roy
> > Zang <roy.zang@nxp.com>; jingoohan1@gmail.com;
> > gustavo.pimentel@synopsys.com; linux-pci@vger.kernel.org;
> > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
> > linux-arm-kernel@lists.infradead.org; linuxppc-dev@lists.ozlabs.org;
> > gregkh@linuxfoundation.org; Z.q. Hou <zhiqiang.hou@nxp.com>;
> > arnd@arndb.de
> > Subject: Re: [PATCH v3 01/11] PCI: designware-ep: Add multiple PFs support
> > for DWC
> >
> > On Mon, Sep 02, 2019 at 11:17:06AM +0800, Xiaowei Bao wrote:
> > > Add multiple PFs support for DWC, different PF have different config
> > > space we use pf-offset property which get from the DTS to access the
> > > different pF
> >
> > This needs to be updated as this no longer comes from the DT.
>
> Yes, thanks
>
> Thanks
> Xiaowei
>
> >
> > > config space.
> > >
> > > Signed-off-by: Xiaowei Bao <xiaowei.bao@nxp.com>
> >
> >
> > We're assuming:
> >
> > - The offset address (func_offset) between PF's in the memory map can be
> > different between different DWC implementations. And also that it's
> > possible for DWC implementations to address PFs without using an offset.
> >
> > - The current approach is preferable to adding DWC EP driver callbacks
> > for writing to the EP config space (e.g. a variant of dw_pcie_writew_dbi
> > that takes a func number).
>
> Even if use the a variant of dw_pcie_writew_dbi, we also need a offset value form
> different platform, due to the different platform may be have different implement
> about this, so I am not sure how to implement the variant of dw_pcie_writew_dbi?
>
> >
> > I'm keen to hear feedback from Jingoo/Gustavo on this.
>
> OK, expect the feedback.
Hi Jingoo/Gustavo,
I'm keen for your review/feedback on this.
Thanks,
Andrew Murray
>
> Thanks
> Xiaowei
>
> >
> > Thanks,
> >
> > Andrew Murray
> >
> > > ---
> > > v2:
> > > - Remove duplicate redundant code.
> > > - Reimplement the PF config space access way.
> > > v3:
> > > - Integrate duplicate code for func_select.
> > > - Move PCIE_ATU_FUNC_NUM(pf) (pf << 20) to ((pf) << 20).
> > > - Add the comments for func_conf_select function.
> > >
> > > drivers/pci/controller/dwc/pcie-designware-ep.c | 123
> > ++++++++++++++++--------
> > > drivers/pci/controller/dwc/pcie-designware.c | 59 ++++++++----
> > > drivers/pci/controller/dwc/pcie-designware.h | 18 +++-
> > > 3 files changed, 142 insertions(+), 58 deletions(-)
> > >
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > index 65f4792..eb851c2 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> > > @@ -19,12 +19,26 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
> > > pci_epc_linkup(epc);
> > > }
> > >
> > > -static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno
> > bar,
> > > - int flags)
> > > +static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8
> > > +func_no) {
> > > + unsigned int func_offset = 0;
> > > +
> > > + if (ep->ops->func_conf_select)
> > > + func_offset = ep->ops->func_conf_select(ep, func_no);
> > > +
> > > + return func_offset;
> > > +}
> > > +
> > > +static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no,
> > > + enum pci_barno bar, int flags)
> > > {
> > > u32 reg;
> > > + unsigned int func_offset = 0;
> > > + struct dw_pcie_ep *ep = &pci->ep;
> > > +
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > >
> > > - reg = PCI_BASE_ADDRESS_0 + (4 * bar);
> > > + reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
> > > dw_pcie_dbi_ro_wr_en(pci);
> > > dw_pcie_writel_dbi2(pci, reg, 0x0);
> > > dw_pcie_writel_dbi(pci, reg, 0x0);
> > > @@ -37,7 +51,12 @@ static void __dw_pcie_ep_reset_bar(struct dw_pcie
> > > *pci, enum pci_barno bar,
> > >
> > > void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) {
> > > - __dw_pcie_ep_reset_bar(pci, bar, 0);
> > > + u8 func_no, funcs;
> > > +
> > > + funcs = pci->ep.epc->max_functions;
> > > +
> > > + for (func_no = 0; func_no < funcs; func_no++)
> > > + __dw_pcie_ep_reset_bar(pci, func_no, bar, 0);
> > > }
> > >
> > > static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
> > > @@ -45,28 +64,31 @@ static int dw_pcie_ep_write_header(struct pci_epc
> > > *epc, u8 func_no, {
> > > struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > + unsigned int func_offset = 0;
> > > +
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > >
> > > dw_pcie_dbi_ro_wr_en(pci);
> > > - dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid);
> > > - dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid);
> > > - dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid);
> > > - dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code);
> > > - dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE,
> > > + dw_pcie_writew_dbi(pci, func_offset + PCI_VENDOR_ID, hdr->vendorid);
> > > + dw_pcie_writew_dbi(pci, func_offset + PCI_DEVICE_ID, hdr->deviceid);
> > > + dw_pcie_writeb_dbi(pci, func_offset + PCI_REVISION_ID, hdr->revid);
> > > + dw_pcie_writeb_dbi(pci, func_offset + PCI_CLASS_PROG,
> > hdr->progif_code);
> > > + dw_pcie_writew_dbi(pci, func_offset + PCI_CLASS_DEVICE,
> > > hdr->subclass_code | hdr->baseclass_code << 8);
> > > - dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE,
> > > + dw_pcie_writeb_dbi(pci, func_offset + PCI_CACHE_LINE_SIZE,
> > > hdr->cache_line_size);
> > > - dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID,
> > > + dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_VENDOR_ID,
> > > hdr->subsys_vendor_id);
> > > - dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id);
> > > - dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN,
> > > + dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_ID,
> > hdr->subsys_id);
> > > + dw_pcie_writeb_dbi(pci, func_offset + PCI_INTERRUPT_PIN,
> > > hdr->interrupt_pin);
> > > dw_pcie_dbi_ro_wr_dis(pci);
> > >
> > > return 0;
> > > }
> > >
> > > -static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum
> > pci_barno bar,
> > > - dma_addr_t cpu_addr,
> > > +static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no,
> > > + enum pci_barno bar, dma_addr_t cpu_addr,
> > > enum dw_pcie_as_type as_type)
> > > {
> > > int ret;
> > > @@ -79,7 +101,7 @@ static int dw_pcie_ep_inbound_atu(struct
> > dw_pcie_ep *ep, enum pci_barno bar,
> > > return -EINVAL;
> > > }
> > >
> > > - ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr,
> > > + ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, bar,
> > > +cpu_addr,
> > > as_type);
> > > if (ret < 0) {
> > > dev_err(pci->dev, "Failed to program IB window\n"); @@ -92,7
> > +114,8
> > > @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum
> > pci_barno bar,
> > > return 0;
> > > }
> > >
> > > -static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t
> > > phys_addr,
> > > +static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,
> > > + phys_addr_t phys_addr,
> > > u64 pci_addr, size_t size)
> > > {
> > > u32 free_win;
> > > @@ -104,8 +127,8 @@ static int dw_pcie_ep_outbound_atu(struct
> > dw_pcie_ep *ep, phys_addr_t phys_addr,
> > > return -EINVAL;
> > > }
> > >
> > > - dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM,
> > > - phys_addr, pci_addr, size);
> > > + dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win,
> > PCIE_ATU_TYPE_MEM,
> > > + phys_addr, pci_addr, size);
> > >
> > > set_bit(free_win, ep->ob_window_map);
> > > ep->outbound_addr[free_win] = phys_addr; @@ -121,7 +144,7 @@
> > static
> > > void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
> > > enum pci_barno bar = epf_bar->barno;
> > > u32 atu_index = ep->bar_to_atu[bar];
> > >
> > > - __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags);
> > > + __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags);
> > >
> > > dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
> > > clear_bit(atu_index, ep->ib_window_map); @@ -137,14 +160,20 @@
> > > static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
> > > size_t size = epf_bar->size;
> > > int flags = epf_bar->flags;
> > > enum dw_pcie_as_type as_type;
> > > - u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
> > > + u32 reg;
> > > + unsigned int func_offset = 0;
> > > +
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > + reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
> > >
> > > if (!(flags & PCI_BASE_ADDRESS_SPACE))
> > > as_type = DW_PCIE_AS_MEM;
> > > else
> > > as_type = DW_PCIE_AS_IO;
> > >
> > > - ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type);
> > > + ret = dw_pcie_ep_inbound_atu(ep, func_no, bar,
> > > + epf_bar->phys_addr, as_type);
> > > if (ret)
> > > return ret;
> > >
> > > @@ -202,7 +231,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc
> > *epc, u8 func_no,
> > > struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > >
> > > - ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size);
> > > + ret = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size);
> > > if (ret) {
> > > dev_err(pci->dev, "Failed to enable address\n");
> > > return ret;
> > > @@ -216,11 +245,14 @@ static int dw_pcie_ep_get_msi(struct pci_epc
> > *epc, u8 func_no)
> > > struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > u32 val, reg;
> > > + unsigned int func_offset = 0;
> > >
> > > if (!ep->msi_cap)
> > > return -EINVAL;
> > >
> > > - reg = ep->msi_cap + PCI_MSI_FLAGS;
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_FLAGS;
> > > val = dw_pcie_readw_dbi(pci, reg);
> > > if (!(val & PCI_MSI_FLAGS_ENABLE))
> > > return -EINVAL;
> > > @@ -235,11 +267,14 @@ static int dw_pcie_ep_set_msi(struct pci_epc
> > *epc, u8 func_no, u8 interrupts)
> > > struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > u32 val, reg;
> > > + unsigned int func_offset = 0;
> > >
> > > if (!ep->msi_cap)
> > > return -EINVAL;
> > >
> > > - reg = ep->msi_cap + PCI_MSI_FLAGS;
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_FLAGS;
> > > val = dw_pcie_readw_dbi(pci, reg);
> > > val &= ~PCI_MSI_FLAGS_QMASK;
> > > val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK; @@ -255,11 +290,14
> > > @@ static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
> > > struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > u32 val, reg;
> > > + unsigned int func_offset = 0;
> > >
> > > if (!ep->msix_cap)
> > > return -EINVAL;
> > >
> > > - reg = ep->msix_cap + PCI_MSIX_FLAGS;
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > + reg = ep->msix_cap + func_offset + PCI_MSIX_FLAGS;
> > > val = dw_pcie_readw_dbi(pci, reg);
> > > if (!(val & PCI_MSIX_FLAGS_ENABLE))
> > > return -EINVAL;
> > > @@ -274,11 +312,14 @@ static int dw_pcie_ep_set_msix(struct pci_epc
> > *epc, u8 func_no, u16 interrupts)
> > > struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > u32 val, reg;
> > > + unsigned int func_offset = 0;
> > >
> > > if (!ep->msix_cap)
> > > return -EINVAL;
> > >
> > > - reg = ep->msix_cap + PCI_MSIX_FLAGS;
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > + reg = ep->msix_cap + func_offset + PCI_MSIX_FLAGS;
> > > val = dw_pcie_readw_dbi(pci, reg);
> > > val &= ~PCI_MSIX_FLAGS_QSIZE;
> > > val |= interrupts;
> > > @@ -365,6 +406,7 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep
> > *ep, u8 func_no,
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > struct pci_epc *epc = ep->epc;
> > > unsigned int aligned_offset;
> > > + unsigned int func_offset = 0;
> > > u16 msg_ctrl, msg_data;
> > > u32 msg_addr_lower, msg_addr_upper, reg;
> > > u64 msg_addr;
> > > @@ -374,20 +416,22 @@ int dw_pcie_ep_raise_msi_irq(struct
> > dw_pcie_ep *ep, u8 func_no,
> > > if (!ep->msi_cap)
> > > return -EINVAL;
> > >
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
> > > - reg = ep->msi_cap + PCI_MSI_FLAGS;
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_FLAGS;
> > > msg_ctrl = dw_pcie_readw_dbi(pci, reg);
> > > has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
> > > - reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_ADDRESS_LO;
> > > msg_addr_lower = dw_pcie_readl_dbi(pci, reg);
> > > if (has_upper) {
> > > - reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_ADDRESS_HI;
> > > msg_addr_upper = dw_pcie_readl_dbi(pci, reg);
> > > - reg = ep->msi_cap + PCI_MSI_DATA_64;
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_DATA_64;
> > > msg_data = dw_pcie_readw_dbi(pci, reg);
> > > } else {
> > > msg_addr_upper = 0;
> > > - reg = ep->msi_cap + PCI_MSI_DATA_32;
> > > + reg = ep->msi_cap + func_offset + PCI_MSI_DATA_32;
> > > msg_data = dw_pcie_readw_dbi(pci, reg);
> > > }
> > > aligned_offset = msg_addr_lower & (epc->mem->page_size - 1); @@
> > > -406,11 +450,12 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep,
> > > u8 func_no, }
> > >
> > > int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
> > > - u16 interrupt_num)
> > > + u16 interrupt_num)
> > > {
> > > struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> > > struct pci_epc *epc = ep->epc;
> > > u16 tbl_offset, bir;
> > > + unsigned int func_offset = 0;
> > > u32 bar_addr_upper, bar_addr_lower;
> > > u32 msg_addr_upper, msg_addr_lower;
> > > u32 reg, msg_data, vec_ctrl;
> > > @@ -418,12 +463,14 @@ int dw_pcie_ep_raise_msix_irq(struct
> > dw_pcie_ep *ep, u8 func_no,
> > > void __iomem *msix_tbl;
> > > int ret;
> > >
> > > - reg = ep->msix_cap + PCI_MSIX_TABLE;
> > > + func_offset = dw_pcie_ep_func_select(ep, func_no);
> > > +
> > > + reg = ep->msix_cap + func_offset + PCI_MSIX_TABLE;
> > > tbl_offset = dw_pcie_readl_dbi(pci, reg);
> > > bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
> > > tbl_offset &= PCI_MSIX_TABLE_OFFSET;
> > >
> > > - reg = PCI_BASE_ADDRESS_0 + (4 * bir);
> > > + reg = PCI_BASE_ADDRESS_0 + func_offset + (4 * bir);
> > > bar_addr_upper = 0;
> > > bar_addr_lower = dw_pcie_readl_dbi(pci, reg);
> > > reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
> > @@
> > > -559,13 +606,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
> > > ep->epc = epc;
> > > epc_set_drvdata(epc, ep);
> > >
> > > - if (ep->ops->ep_init)
> > > - ep->ops->ep_init(ep);
> > > -
> > > ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
> > > if (ret < 0)
> > > epc->max_functions = 1;
> > >
> > > + if (ep->ops->ep_init)
> > > + ep->ops->ep_init(ep);
> > > +
> > > ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
> > > ep->page_size);
> > > if (ret < 0) {
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware.c
> > > b/drivers/pci/controller/dwc/pcie-designware.c
> > > index 143cb6c..ede2e75 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware.c
> > > +++ b/drivers/pci/controller/dwc/pcie-designware.c
> > > @@ -238,9 +238,10 @@ static void dw_pcie_writel_ob_unroll(struct
> > dw_pcie *pci, u32 index, u32 reg,
> > > dw_pcie_writel_atu(pci, offset + reg, val); }
> > >
> > > -static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int
> > index,
> > > - int type, u64 cpu_addr,
> > > - u64 pci_addr, u32 size)
> > > +static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8
> > func_no,
> > > + int index, int type,
> > > + u64 cpu_addr, u64 pci_addr,
> > > + u32 size)
> > > {
> > > u32 retries, val;
> > >
> > > @@ -255,7 +256,7 @@ static void
> > dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index,
> > > dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
> > > upper_32_bits(pci_addr));
> > > dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
> > > - type);
> > > + type | PCIE_ATU_FUNC_NUM(func_no));
> > > dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
> > > PCIE_ATU_ENABLE);
> > >
> > > @@ -274,8 +275,9 @@ static void
> > dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index,
> > > dev_err(pci->dev, "Outbound iATU is not being enabled\n"); }
> > >
> > > -void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
> > > - u64 cpu_addr, u64 pci_addr, u32 size)
> > > +static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8
> > func_no,
> > > + int index, int type, u64 cpu_addr,
> > > + u64 pci_addr, u32 size)
> > > {
> > > u32 retries, val;
> > >
> > > @@ -283,8 +285,8 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie
> > *pci, int index, int type,
> > > cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
> > >
> > > if (pci->iatu_unroll_enabled) {
> > > - dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr,
> > > - pci_addr, size);
> > > + dw_pcie_prog_outbound_atu_unroll(pci, func_no, index, type,
> > > + cpu_addr, pci_addr, size);
> > > return;
> > > }
> > >
> > > @@ -300,7 +302,8 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie
> > *pci, int index, int type,
> > > lower_32_bits(pci_addr));
> > > dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
> > > upper_32_bits(pci_addr));
> > > - dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type);
> > > + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
> > > + PCIE_ATU_FUNC_NUM(func_no));
> > > dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> > >
> > > /*
> > > @@ -317,6 +320,21 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie
> > *pci, int index, int type,
> > > dev_err(pci->dev, "Outbound iATU is not being enabled\n"); }
> > >
> > > +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
> > > + u64 cpu_addr, u64 pci_addr, u32 size) {
> > > + __dw_pcie_prog_outbound_atu(pci, 0, index, type,
> > > + cpu_addr, pci_addr, size);
> > > +}
> > > +
> > > +void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int
> > index,
> > > + int type, u64 cpu_addr, u64 pci_addr,
> > > + u32 size)
> > > +{
> > > + __dw_pcie_prog_outbound_atu(pci, func_no, index, type,
> > > + cpu_addr, pci_addr, size);
> > > +}
> > > +
> > > static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index,
> > > u32 reg) {
> > > u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index);
> > > @@ -332,8 +350,8 @@ static void dw_pcie_writel_ib_unroll(struct dw_pcie
> > *pci, u32 index, u32 reg,
> > > dw_pcie_writel_atu(pci, offset + reg, val); }
> > >
> > > -static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index,
> > > - int bar, u64 cpu_addr,
> > > +static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, u8
> > func_no,
> > > + int index, int bar, u64 cpu_addr,
> > > enum dw_pcie_as_type as_type) {
> > > int type;
> > > @@ -355,8 +373,10 @@ static int dw_pcie_prog_inbound_atu_unroll(struct
> > dw_pcie *pci, int index,
> > > return -EINVAL;
> > > }
> > >
> > > - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
> > type);
> > > + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
> > type |
> > > + PCIE_ATU_FUNC_NUM(func_no));
> > > dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
> > > + PCIE_ATU_FUNC_NUM_MATCH_EN |
> > > PCIE_ATU_ENABLE |
> > > PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
> > >
> > > @@ -377,14 +397,15 @@ static int
> > dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index,
> > > return -EBUSY;
> > > }
> > >
> > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar,
> > > - u64 cpu_addr, enum dw_pcie_as_type as_type)
> > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > + int bar, u64 cpu_addr,
> > > + enum dw_pcie_as_type as_type)
> > > {
> > > int type;
> > > u32 retries, val;
> > >
> > > if (pci->iatu_unroll_enabled)
> > > - return dw_pcie_prog_inbound_atu_unroll(pci, index, bar,
> > > + return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, bar,
> > > cpu_addr, as_type);
> > >
> > > dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
> > PCIE_ATU_REGION_INBOUND |
> > > @@ -403,9 +424,11 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie
> > *pci, int index, int bar,
> > > return -EINVAL;
> > > }
> > >
> > > - dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type);
> > > - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE
> > > - | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
> > > + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
> > > + PCIE_ATU_FUNC_NUM(func_no));
> > > + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE |
> > > + PCIE_ATU_FUNC_NUM_MATCH_EN |
> > > + PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
> > >
> > > /*
> > > * Make sure ATU enable takes effect before any subsequent config
> > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h
> > > b/drivers/pci/controller/dwc/pcie-designware.h
> > > index 5a18e94..6aca0bb 100644
> > > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > > @@ -71,9 +71,11 @@
> > > #define PCIE_ATU_TYPE_IO 0x2
> > > #define PCIE_ATU_TYPE_CFG0 0x4
> > > #define PCIE_ATU_TYPE_CFG1 0x5
> > > +#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
> > > #define PCIE_ATU_CR2 0x908
> > > #define PCIE_ATU_ENABLE BIT(31)
> > > #define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
> > > +#define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19)
> > > #define PCIE_ATU_LOWER_BASE 0x90C
> > > #define PCIE_ATU_UPPER_BASE 0x910
> > > #define PCIE_ATU_LIMIT 0x914
> > > @@ -206,6 +208,14 @@ struct dw_pcie_ep_ops {
> > > int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
> > > enum pci_epc_irq_type type, u16 interrupt_num);
> > > const struct pci_epc_features* (*get_features)(struct dw_pcie_ep
> > > *ep);
> > > + /*
> > > + * Provide a method to implement the different func config space
> > > + * access for different platform, if different func have different
> > > + * offset, return the offset of func. if use write a register way
> > > + * return a 0, and implement code in callback function of platform
> > > + * driver.
> > > + */
> > > + unsigned int (*func_conf_select)(struct dw_pcie_ep *ep, u8 func_no);
> > > };
> > >
> > > struct dw_pcie_ep {
> > > @@ -277,8 +287,12 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci);
> > > void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
> > > int type, u64 cpu_addr, u64 pci_addr,
> > > u32 size);
> > > -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar,
> > > - u64 cpu_addr, enum dw_pcie_as_type as_type);
> > > +void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int
> > index,
> > > + int type, u64 cpu_addr, u64 pci_addr,
> > > + u32 size);
> > > +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> > > + int bar, u64 cpu_addr,
> > > + enum dw_pcie_as_type as_type);
> > > void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
> > > enum dw_pcie_region_type type);
> > > void dw_pcie_setup(struct dw_pcie *pci);
> > > --
> > > 2.9.5
> > >
> > >
> > > _______________________________________________
> > > linux-arm-kernel mailing list
> > > linux-arm-kernel@lists.infradead.org
> > > https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists
> > > .infradead.org%2Fmailman%2Flistinfo%2Flinux-arm-kernel&data=02%
> > 7C0
> > >
> > 1%7Cxiaowei.bao%40nxp.com%7C99eef14a525040ed3eab08d72fc244f1%7C
> > 686ea1d
> > >
> > 3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637030383769341270&sd
> > ata=ck2EC
> > > %2FJYCjWErvbUM%2FT%2BoVMANMwyLRI4gVRssdnd04w%3D&reser
> > ved=0
_______________________________________________
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 06/11] PCI: of: Add inbound resource parsing to helpers
From: Gustavo Pimentel @ 2019-09-26 10:43 UTC (permalink / raw)
To: Rob Herring, linux-pci@vger.kernel.org, Bjorn Helgaas,
Lorenzo Pieralisi
Cc: Heiko Stuebner, Karthikeyan Mitran, Linus Walleij,
Thomas Petazzoni, Toan Le, Will Deacon, Ryder Lee, Michal Simek,
linux-rockchip@lists.infradead.org,
bcm-kernel-feedback-list@broadcom.com, Shawn Lin, Ray Jui,
Hou Zhiqiang, Simon Horman, linux-mediatek@lists.infradead.org,
linux-arm-kernel@lists.infradead.org, Scott Branden, Jingoo Han,
rfi@lists.rocketboards.org, linux-renesas-soc@vger.kernel.org,
Tom Joseph, Gustavo Pimentel, Ley Foon Tan
In-Reply-To: <20190924214630.12817-7-robh@kernel.org>
On Tue, Sep 24, 2019 at 22:46:25, Rob Herring <robh@kernel.org> wrote:
> Extend devm_of_pci_get_host_bridge_resources() and
> pci_parse_request_of_pci_ranges() helpers to also parse the inbound
> addresses from DT 'dma-ranges' and populate a resource list with the
> translated addresses. This will help ensure 'dma-ranges' is always
> parsed in a consistent way.
>
> Cc: Jingoo Han <jingoohan1@gmail.com>
> Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: Toan Le <toan@os.amperecomputing.com>
> Cc: Ley Foon Tan <lftan@altera.com>
> Cc: Tom Joseph <tjoseph@cadence.com>
> Cc: Ray Jui <rjui@broadcom.com>
> Cc: Scott Branden <sbranden@broadcom.com>
> Cc: bcm-kernel-feedback-list@broadcom.com
> Cc: Ryder Lee <ryder.lee@mediatek.com>
> Cc: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in>
> Cc: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
> Cc: Simon Horman <horms@verge.net.au>
> Cc: Shawn Lin <shawn.lin@rock-chips.com>
> Cc: Heiko Stuebner <heiko@sntech.de>
> Cc: Michal Simek <michal.simek@xilinx.com>
> Cc: rfi@lists.rocketboards.org
> Cc: linux-mediatek@lists.infradead.org
> Cc: linux-renesas-soc@vger.kernel.org
> Cc: linux-rockchip@lists.infradead.org
> Signed-off-by: Rob Herring <robh@kernel.org>
> ---
> .../pci/controller/dwc/pcie-designware-host.c | 3 +-
> drivers/pci/controller/pci-aardvark.c | 2 +-
> drivers/pci/controller/pci-ftpci100.c | 3 +-
> drivers/pci/controller/pci-host-common.c | 2 +-
> drivers/pci/controller/pci-v3-semi.c | 2 +-
> drivers/pci/controller/pci-versatile.c | 2 +-
> drivers/pci/controller/pci-xgene.c | 1 +
> drivers/pci/controller/pcie-altera.c | 2 +-
> drivers/pci/controller/pcie-cadence-host.c | 2 +-
> drivers/pci/controller/pcie-iproc-platform.c | 1 +
> drivers/pci/controller/pcie-mediatek.c | 2 +-
> drivers/pci/controller/pcie-mobiveil.c | 4 +-
> drivers/pci/controller/pcie-rcar.c | 3 +-
> drivers/pci/controller/pcie-rockchip-host.c | 3 +-
> drivers/pci/controller/pcie-xilinx-nwl.c | 2 +-
> drivers/pci/controller/pcie-xilinx.c | 2 +-
> drivers/pci/of.c | 44 ++++++++++++++++++-
> drivers/pci/pci.h | 8 +++-
> include/linux/pci.h | 2 +
> 19 files changed, 72 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> index f93252d0da5b..0743ae64bb0f 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> @@ -342,7 +342,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
> return -ENOMEM;
>
> ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
> - &bridge->windows, &pp->io_base);
> + &bridge->windows, &bridge->dma_ranges,
> + &pp->io_base);
> if (ret)
> return ret;
>
> diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
> index ff3af3d34028..c00b391529f3 100644
> --- a/drivers/pci/controller/pci-aardvark.c
> +++ b/drivers/pci/controller/pci-aardvark.c
> @@ -940,7 +940,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
> }
>
> ret = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
> - &bus);
> + &bridge->dma_ranges, &bus);
> if (ret) {
> dev_err(dev, "Failed to parse resources\n");
> return ret;
> diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c
> index bf5ece5d9291..3e07a8203736 100644
> --- a/drivers/pci/controller/pci-ftpci100.c
> +++ b/drivers/pci/controller/pci-ftpci100.c
> @@ -481,7 +481,8 @@ static int faraday_pci_probe(struct platform_device *pdev)
> return PTR_ERR(p->base);
>
> ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
> - &res, &io_base);
> + &res, &host->dma_ranges,
> + &io_base);
> if (ret)
> return ret;
>
> diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
> index c742881b5061..183381c19ee8 100644
> --- a/drivers/pci/controller/pci-host-common.c
> +++ b/drivers/pci/controller/pci-host-common.c
> @@ -27,7 +27,7 @@ static struct pci_config_window *gen_pci_init(struct device *dev,
> struct pci_config_window *cfg;
>
> /* Parse our PCI ranges and request their resources */
> - err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
> + err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
> if (err)
> return ERR_PTR(err);
>
> diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c
> index d219404bad92..090df766faf9 100644
> --- a/drivers/pci/controller/pci-v3-semi.c
> +++ b/drivers/pci/controller/pci-v3-semi.c
> @@ -794,7 +794,7 @@ static int v3_pci_probe(struct platform_device *pdev)
> return PTR_ERR(v3->config_base);
>
> ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
> - &io_base);
> + &host->dma_ranges, &io_base);
> if (ret)
> return ret;
>
> diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c
> index 237b1abb26f2..211159f54b4a 100644
> --- a/drivers/pci/controller/pci-versatile.c
> +++ b/drivers/pci/controller/pci-versatile.c
> @@ -93,7 +93,7 @@ static int versatile_pci_probe(struct platform_device *pdev)
> if (IS_ERR(versatile_cfg_base[1]))
> return PTR_ERR(versatile_cfg_base[1]);
>
> - ret = pci_parse_request_of_pci_ranges(dev, &pci_res, NULL);
> + ret = pci_parse_request_of_pci_ranges(dev, &pci_res, NULL, NULL);
> if (ret)
> return ret;
>
> diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c
> index ffda3e8b4742..11f27c42c06a 100644
> --- a/drivers/pci/controller/pci-xgene.c
> +++ b/drivers/pci/controller/pci-xgene.c
> @@ -635,6 +635,7 @@ static int xgene_pcie_probe(struct platform_device *pdev)
> return ret;
>
> ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
> + &bridge->dma_ranges,
> &iobase);
> if (ret)
> return ret;
> diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c
> index 2ed00babff5a..fde6a3b16fb9 100644
> --- a/drivers/pci/controller/pcie-altera.c
> +++ b/drivers/pci/controller/pcie-altera.c
> @@ -801,7 +801,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
> }
>
> ret = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
> - NULL);
> + &bridge->dma_ranges, NULL);
> if (ret) {
> dev_err(dev, "Failed add resources\n");
> return ret;
> diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c
> index 97e251090b4f..a8f7a6284c3e 100644
> --- a/drivers/pci/controller/pcie-cadence-host.c
> +++ b/drivers/pci/controller/pcie-cadence-host.c
> @@ -211,7 +211,7 @@ static int cdns_pcie_host_init(struct device *dev,
> int err;
>
> /* Parse our PCI ranges and request their resources */
> - err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
> + err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
> if (err)
> return err;
>
> diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c
> index 5a3550b6bb29..62eee2d17f43 100644
> --- a/drivers/pci/controller/pcie-iproc-platform.c
> +++ b/drivers/pci/controller/pcie-iproc-platform.c
> @@ -101,6 +101,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
> }
>
> ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources,
> + &bridge->dma_ranges,
> &iobase);
> if (ret) {
> dev_err(dev, "unable to get PCI host bridge resources\n");
> diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
> index cea4112b5309..f3996eed152d 100644
> --- a/drivers/pci/controller/pcie-mediatek.c
> +++ b/drivers/pci/controller/pcie-mediatek.c
> @@ -1032,7 +1032,7 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
> int err;
>
> err = pci_parse_request_of_pci_ranges(dev, windows,
> - &bus);
> + &host->dma_ranges, &bus);
> if (err)
> return err;
>
> diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c
> index 672e633601c7..93064faaa656 100644
> --- a/drivers/pci/controller/pcie-mobiveil.c
> +++ b/drivers/pci/controller/pcie-mobiveil.c
> @@ -875,7 +875,9 @@ static int mobiveil_pcie_probe(struct platform_device *pdev)
>
> /* parse the host bridge base addresses from the device tree file */
> ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
> - &pcie->resources, &iobase);
> + &pcie->resources,
> + &bridge->dma_ranges,
> + &iobase);
> if (ret) {
> dev_err(dev, "Getting bridge resources failed\n");
> return ret;
> diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar.c
> index f6a669a9af41..b8d6e86a5539 100644
> --- a/drivers/pci/controller/pcie-rcar.c
> +++ b/drivers/pci/controller/pcie-rcar.c
> @@ -1138,7 +1138,8 @@ static int rcar_pcie_probe(struct platform_device *pdev)
> pcie->dev = dev;
> platform_set_drvdata(pdev, pcie);
>
> - err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL);
> + err = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
> + &bridge->dma_ranges, NULL);
> if (err)
> goto err_free_bridge;
>
> diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
> index 8d20f1793a61..093b816b9dc9 100644
> --- a/drivers/pci/controller/pcie-rockchip-host.c
> +++ b/drivers/pci/controller/pcie-rockchip-host.c
> @@ -996,7 +996,8 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
> goto err_deinit_port;
>
> err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
> - &res, &io_base);
> + &res, &bridge->dma_ranges,
> + &io_base);
> if (err)
> goto err_remove_irq_domain;
>
> diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c
> index 45c0f344ccd1..5c9132a02d15 100644
> --- a/drivers/pci/controller/pcie-xilinx-nwl.c
> +++ b/drivers/pci/controller/pcie-xilinx-nwl.c
> @@ -846,7 +846,7 @@ static int nwl_pcie_probe(struct platform_device *pdev)
> }
>
> err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
> - &iobase);
> + &bridge->dma_ranges, &iobase);
> if (err) {
> dev_err(dev, "Getting bridge resources failed\n");
> return err;
> diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c
> index 5bf3af3b28e6..a95faca30291 100644
> --- a/drivers/pci/controller/pcie-xilinx.c
> +++ b/drivers/pci/controller/pcie-xilinx.c
> @@ -648,7 +648,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
> }
>
> err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
> - &iobase);
> + &bridge->dma_ranges, &iobase);
> if (err) {
> dev_err(dev, "Getting bridge resources failed\n");
> return err;
> diff --git a/drivers/pci/of.c b/drivers/pci/of.c
> index bc7b27a28795..8d31e078461f 100644
> --- a/drivers/pci/of.c
> +++ b/drivers/pci/of.c
> @@ -257,7 +257,9 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_only);
> */
> int devm_of_pci_get_host_bridge_resources(struct device *dev,
> unsigned char busno, unsigned char bus_max,
> - struct list_head *resources, resource_size_t *io_base)
> + struct list_head *resources,
> + struct list_head *ib_resources,
> + resource_size_t *io_base)
> {
> struct device_node *dev_node = dev->of_node;
> struct resource *res, tmp_res;
> @@ -340,6 +342,42 @@ int devm_of_pci_get_host_bridge_resources(struct device *dev,
> pci_add_resource_offset(resources, res, res->start - range.pci_addr);
> }
>
> + /* Check for dma-ranges property */
> + if (!ib_resources)
> + return 0;
> + err = of_pci_dma_range_parser_init(&parser, dev_node);
> + if (err)
> + return 0;
> +
> + dev_dbg(dev, "Parsing dma-ranges property...\n");
> + for_each_of_pci_range(&parser, &range) {
> + /*
> + * If we failed translation or got a zero-sized region
> + * then skip this range
> + */
> + if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM) ||
> + range.cpu_addr == OF_BAD_ADDR || range.size == 0)
> + continue;
> +
> + dev_info(dev, "IB MEM %#010llx..%#010llx -> %#010llx\n",
> + range.cpu_addr,
> + range.cpu_addr + range.size - 1, range.pci_addr);
> +
> +
> + err = of_pci_range_to_resource(&range, dev_node, &tmp_res);
> + if (err)
> + continue;
> +
> + res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL);
> + if (!res) {
> + err = -ENOMEM;
> + goto failed;
> + }
> +
> + pci_add_resource_offset(ib_resources, res,
> + res->start - range.pci_addr);
> + }
> +
> return 0;
>
> failed:
> @@ -482,6 +520,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci);
>
> int pci_parse_request_of_pci_ranges(struct device *dev,
> struct list_head *resources,
> + struct list_head *ib_resources,
> struct resource **bus_range)
> {
> int err, res_valid = 0;
> @@ -489,8 +528,9 @@ int pci_parse_request_of_pci_ranges(struct device *dev,
> struct resource_entry *win, *tmp;
>
> INIT_LIST_HEAD(resources);
> + INIT_LIST_HEAD(ib_resources);
> err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, resources,
> - &iobase);
> + ib_resources, &iobase);
> if (err)
> return err;
>
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index d22d1b807701..790458953c54 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -582,11 +582,15 @@ of_pci_get_max_link_speed(struct device_node *node)
> #if defined(CONFIG_OF_ADDRESS)
> int devm_of_pci_get_host_bridge_resources(struct device *dev,
> unsigned char busno, unsigned char bus_max,
> - struct list_head *resources, resource_size_t *io_base);
> + struct list_head *resources,
> + struct list_head *ib_resources,
> + resource_size_t *io_base);
> #else
> static inline int devm_of_pci_get_host_bridge_resources(struct device *dev,
> unsigned char busno, unsigned char bus_max,
> - struct list_head *resources, resource_size_t *io_base)
> + struct list_head *resources,
> + struct list_head *ib_resources,
> + resource_size_t *io_base);
> {
> return -EINVAL;
> }
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 82e4cd1b7ac3..d57389b86f92 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -2312,6 +2312,7 @@ void pci_release_bus_of_node(struct pci_bus *bus);
> struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus);
> int pci_parse_request_of_pci_ranges(struct device *dev,
> struct list_head *resources,
> + struct list_head *ib_resources,
> struct resource **bus_range);
>
> /* Arch may override this (weak) */
> @@ -2326,6 +2327,7 @@ static inline struct irq_domain *
> pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
> static inline int pci_parse_request_of_pci_ranges(struct device *dev,
> struct list_head *resources,
> + struct list_head *ib_resources,
> struct resource **bus_range)
> {
> return -EINVAL;
> --
> 2.20.1
Seems ok to me.
Acked-by: Gustavo Pimentel <gustavo.pimentel@synopsys.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 00/11] of: Fix DMA configuration for non-DT masters
From: Nicolas Saenz Julienne @ 2019-09-26 10:44 UTC (permalink / raw)
To: Rob Herring, Robin Murphy
Cc: devicetree, Matthias Brugger, linux-arm-msm, linux-wireless,
linux-kernel@vger.kernel.org, dri-devel, etnaviv, linux-tegra,
Florian Fainelli, Stefan Wahren, james.quinlan, linux-pci,
open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM, xen-devel,
Dan Williams, freedreno, Frank Rowand,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Linux Media Mailing List
In-Reply-To: <CAL_JsqKKYcHPnA80ZwLY=Sk3e5MqrimedUhWQ5+iuPZXQxYHdA@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 1872 bytes --]
> > > > Robin, have you looked into supporting multiple dma-ranges? It's the
> > > > next thing
> > > > we need for BCM STB's PCIe. I'll have a go at it myself if nothing is in
> > > > the
> > > > works already.
> > >
> > > Multiple dma-ranges as far as configuring inbound windows should work
> > > already other than the bug when there's any parent translation. But if
> > > you mean supporting multiple DMA offsets and masks per device in the
> > > DMA API, there's nothing in the works yet.
Sorry, I meant supporting multiple DMA offsets[1]. I think I could still make
it with a single DMA mask though.
> > There's also the in-between step of making of_dma_get_range() return a
> > size based on all the dma-ranges entries rather than only the first one
> > - otherwise, something like [1] can lead to pretty unworkable default
> > masks. We implemented that when doing acpi_dma_get_range(), it's just
> > that the OF counterpart never caught up.
>
> Right. I suppose we assume any holes in the ranges are addressable by
> the device but won't get used for other reasons (such as no memory
> there). However, to be correct, the range of the dma offset plus mask
> would need to be within the min start and max end addresses. IOW,
> while we need to round up (0xa_8000_0000 - 0x2c1c_0000) to the next
> power of 2, the 'correct' thing to do is round down.
IIUC I also have this issue on my list. The RPi4 PCIe block has an integration
bug that only allows DMA to the lower 3GB. With dma-ranges of size 0xc000_0000
you get a 32bit DMA mask wich is not what you need. So far I faked it in the
device-tree but I guess it be better to add an extra check in
of_dma_configure(), decrease the mask and print some kind of warning stating
that DMA addressing is suboptimal.
Regards,
Nicolas
[1] https://lkml.org/lkml/2018/9/19/641
[-- Attachment #1.2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 488 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
* [PATCH v4 00/19] spi: introduce `struct spi_delay` data-type
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
Initially, I started this patchset thinking: "we need a new delay for
something-something" (in case someone is curios, we needed a CS-hold-time
when CS is asserted, because the CS wakes a chip from sleep-mode).
Then I added the delay, and felt a bit dirty-inside about adding a new one
(just like that), and decided to look at maybe cleaning things up a bit,
and a few days/weeks later, I got here.
It's based on top of Jonathan's `iio/togreg` branch which also includes the
ADIS driver library changes for `cs_change_delay`.
The current v4 tries to be a complete change as possible about converting
all delays to the `struct delay` data-type; at least in the scope of the
SPI subsystem. Many external users of the SPI subsystem use the
`delay_usecs` field to specify delays for transfers; which is why this
field is still kept temporarily for backwards compatibility until all other
delays are switched to `delay`.
Thanks
Alex
Changelog v3 -> v4:
* re-unify all `spi_delay` changes into a single big series spanning SPI &
IIO subsystems; patches are based on top of IIO, so patch
`drivers/iio/imu/adis.c` does not apply on top of SPI/for-next tree
Changelog v2 -> v3:
* squash patches 3 & 4 into a single patch; otherwise the kernel is in
broken state between those 2 patches
Changelog v1 -> v2:
* split away from the RFC patchset, which aims to be a broader explanation
for this changeset; parts of v1 are not 100% defined yet, and may require
some discussion and refinement.
Alexandru Ardelean (19):
spi: move `cs_change_delay` backwards compat logic outside switch
spi: introduce spi_delay struct as "value + unit" & spi_delay_exec()
spi: make `cs_change_delay` the first user of the `spi_delay` logic
spi: sprd: convert transfer word delay to spi_delay struct
spi: orion: use new `word_delay` field for SPI transfers
spi: spidev: use new `word_delay` field for spi transfers
spi: core,atmel: convert `word_delay_usecs` -> `word_delay` for
spi_device
spi: introduce `delay` field for `spi_transfer` +
spi_transfer_delay_exec()
spi: use new `spi_transfer_delay_exec` helper where straightforward
spi: tegra114: use `spi_transfer_delay_exec` helper
spi: tegra20-sflash: use to new `spi_transfer_delay_exec`
spi: spi-loopback-test: use new `delay` field
spi: spidev: use new `delay` field for spi transfers
spi: tegra114: change format for `spi_set_cs_timing()` function
spi: implement SW control for CS times
spi: spi-fsl-espi: convert transfer delay to `spi_delay` format
spi: spi-falcon: extend warning to `delay` as well
spi: bcm63xx: extend error condition to `delay` as well
spi: spi-axi: extend support for the `delay` field
drivers/iio/imu/adis.c | 24 ++---
drivers/spi/spi-atmel.c | 29 +++++-
drivers/spi/spi-axi-spi-engine.c | 16 +++-
drivers/spi/spi-bcm63xx-hsspi.c | 3 +-
drivers/spi/spi-bcm63xx.c | 2 +-
drivers/spi/spi-cavium.c | 3 +-
drivers/spi/spi-falcon.c | 2 +-
drivers/spi/spi-fsl-dspi.c | 3 +-
drivers/spi/spi-fsl-espi.c | 19 +++-
drivers/spi/spi-fsl-spi.c | 3 +-
drivers/spi/spi-loopback-test.c | 12 ++-
drivers/spi/spi-mpc512x-psc.c | 3 +-
drivers/spi/spi-mpc52xx-psc.c | 3 +-
drivers/spi/spi-omap-100k.c | 3 +-
drivers/spi/spi-orion.c | 6 +-
drivers/spi/spi-pl022.c | 25 +++--
drivers/spi/spi-sc18is602.c | 3 +-
drivers/spi/spi-sh-hspi.c | 3 +-
drivers/spi/spi-sprd.c | 15 ++-
drivers/spi/spi-tegra114.c | 39 ++++----
drivers/spi/spi-tegra20-sflash.c | 5 +-
drivers/spi/spi-topcliff-pch.c | 7 +-
drivers/spi/spi-txx9.c | 3 +-
drivers/spi/spi-xcomm.c | 3 +-
drivers/spi/spi.c | 158 +++++++++++++++++++++++++------
drivers/spi/spidev.c | 6 +-
include/linux/spi/spi.h | 66 +++++++++----
27 files changed, 323 insertions(+), 141 deletions(-)
--
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
* [PATCH v4 01/19] spi: move `cs_change_delay` backwards compat logic outside switch
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
The `cs_change_delay` backwards compatibility value could be moved outside
of the switch statement.
The only reason to do it, is to make the next patches easier to diff.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 75ac046cae52..c90e02e6d62f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1114,16 +1114,15 @@ static void _spi_transfer_cs_change_delay(struct spi_message *msg,
u32 hz;
/* return early on "fast" mode - for everything but USECS */
- if (!delay && unit != SPI_DELAY_UNIT_USECS)
+ if (!delay) {
+ if (unit == SPI_DELAY_UNIT_USECS)
+ _spi_transfer_delay_ns(10000);
return;
+ }
switch (unit) {
case SPI_DELAY_UNIT_USECS:
- /* for compatibility use default of 10us */
- if (!delay)
- delay = 10000;
- else
- delay *= 1000;
+ delay *= 1000;
break;
case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
break;
--
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 v4 03/19] spi: make `cs_change_delay` the first user of the `spi_delay` logic
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
Since the logic for `spi_delay` struct + `spi_delay_exec()` has been copied
from the `cs_change_delay` logic, it's natural to make this delay, the
first user.
The `cs_change_delay` logic requires that the default remain 10 uS, in case
it is unspecified/unconfigured. So, there is some special handling needed
to do that.
The ADIS library is one of the few users of the new `cs_change_delay`
parameter for an spi_transfer.
The introduction of the `spi_delay` struct, requires that the users of of
`cs_change_delay` get an update. This change also updates the ADIS library.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/iio/imu/adis.c | 24 ++++++++++++------------
drivers/spi/spi.c | 28 +++++++---------------------
include/linux/spi/spi.h | 4 +---
3 files changed, 20 insertions(+), 36 deletions(-)
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index 1631c255deab..2cd2cc2316c6 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -39,24 +39,24 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
- .cs_change_delay = adis->data->cs_change_delay,
- .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
+ .cs_change_delay.value = adis->data->cs_change_delay,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 2,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
- .cs_change_delay = adis->data->cs_change_delay,
- .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
+ .cs_change_delay.value = adis->data->cs_change_delay,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 4,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
- .cs_change_delay = adis->data->cs_change_delay,
- .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
+ .cs_change_delay.value = adis->data->cs_change_delay,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 6,
.bits_per_word = 8,
@@ -139,16 +139,16 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
- .cs_change_delay = adis->data->cs_change_delay,
- .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
+ .cs_change_delay.value = adis->data->cs_change_delay,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 2,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->read_delay,
- .cs_change_delay = adis->data->cs_change_delay,
- .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
+ .cs_change_delay.value = adis->data->cs_change_delay,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 4,
.rx_buf = adis->rx,
@@ -156,8 +156,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->read_delay,
- .cs_change_delay = adis->data->cs_change_delay,
- .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
+ .cs_change_delay.value = adis->data->cs_change_delay,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.rx_buf = adis->rx + 2,
.bits_per_word = 8,
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1883de8ffa82..d0bf0ffca042 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1160,9 +1160,9 @@ EXPORT_SYMBOL_GPL(spi_delay_exec);
static void _spi_transfer_cs_change_delay(struct spi_message *msg,
struct spi_transfer *xfer)
{
- u32 delay = xfer->cs_change_delay;
- u32 unit = xfer->cs_change_delay_unit;
- u32 hz;
+ u32 delay = xfer->cs_change_delay.value;
+ u32 unit = xfer->cs_change_delay.unit;
+ int ret;
/* return early on "fast" mode - for everything but USECS */
if (!delay) {
@@ -1171,27 +1171,13 @@ static void _spi_transfer_cs_change_delay(struct spi_message *msg,
return;
}
- switch (unit) {
- case SPI_DELAY_UNIT_USECS:
- delay *= 1000;
- break;
- case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
- break;
- case SPI_DELAY_UNIT_SCK:
- /* if there is no effective speed know, then approximate
- * by underestimating with half the requested hz
- */
- hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2;
- delay *= DIV_ROUND_UP(1000000000, hz);
- break;
- default:
+ ret = spi_delay_exec(&xfer->cs_change_delay, xfer);
+ if (ret) {
dev_err_once(&msg->spi->dev,
"Use of unsupported delay unit %i, using default of 10us\n",
- xfer->cs_change_delay_unit);
- delay = 10000;
+ unit);
+ _spi_transfer_delay_ns(10000);
}
- /* now sleep for the requested amount of time */
- _spi_transfer_delay_ns(delay);
}
/*
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index c18cfa7cda35..9ded3f44d58e 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -754,7 +754,6 @@ extern void spi_res_release(struct spi_controller *ctlr,
* @cs_change: affects chipselect after this transfer completes
* @cs_change_delay: delay between cs deassert and assert when
* @cs_change is set and @spi_transfer is not the last in @spi_message
- * @cs_change_delay_unit: unit of cs_change_delay
* @delay_usecs: microseconds to delay after this transfer before
* (optionally) changing the chipselect status, then starting
* the next transfer or completing this @spi_message.
@@ -847,8 +846,7 @@ struct spi_transfer {
u8 bits_per_word;
u8 word_delay_usecs;
u16 delay_usecs;
- u16 cs_change_delay;
- u8 cs_change_delay_unit;
+ struct spi_delay cs_change_delay;
u32 speed_hz;
u16 word_delay;
--
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 v4 02/19] spi: introduce spi_delay struct as "value + unit" & spi_delay_exec()
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
There are plenty of delays that have been introduced in SPI core. Most of
them are in micro-seconds, some need to be in nano-seconds, and some in
clock-cycles.
For some of these delays (related to transfers & CS timing) it may make
sense to have a `spi_delay` struct that abstracts these a bit.
The important element of these delays [for unification] seems to be the
`unit` of the delay.
It looks like micro-seconds is good enough for most people, but every-once
in a while, some delays seem to require other units of measurement.
This change adds the `spi_delay` struct & a `spi_delay_exec()` function
that processes a `spi_delay` object/struct to execute the delay.
It's a copy of the `cs_change_delay` mechanism, but without the default
for 10 uS.
The clock-cycle delay unit is a bit special, as it needs to be bound to an
`spi_transfer` object to execute.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi.c | 51 +++++++++++++++++++++++++++++++++++++++++
include/linux/spi/spi.h | 18 ++++++++++++---
2 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index c90e02e6d62f..1883de8ffa82 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1106,6 +1106,57 @@ static void _spi_transfer_delay_ns(u32 ns)
}
}
+static int _spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer)
+{
+ u32 delay = _delay->value;
+ u32 unit = _delay->unit;
+ u32 hz;
+
+ if (!delay)
+ return 0;
+
+ switch (unit) {
+ case SPI_DELAY_UNIT_USECS:
+ delay *= 1000;
+ break;
+ case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
+ break;
+ case SPI_DELAY_UNIT_SCK:
+ /* clock cycles need to be obtained from spi_transfer */
+ if (!xfer)
+ return -EINVAL;
+ /* if there is no effective speed know, then approximate
+ * by underestimating with half the requested hz
+ */
+ hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2;
+ if (!hz)
+ return -EINVAL;
+ delay *= DIV_ROUND_UP(1000000000, hz);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return delay;
+}
+
+int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer)
+{
+ int delay;
+
+ if (!_delay)
+ return -EINVAL;
+
+ delay = _spi_delay_to_ns(_delay, xfer);
+ if (delay < 0)
+ return delay;
+
+ _spi_transfer_delay_ns(delay);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_delay_exec);
+
static void _spi_transfer_cs_change_delay(struct spi_message *msg,
struct spi_transfer *xfer)
{
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index af4f265d0f67..c18cfa7cda35 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -89,6 +89,21 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
#define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \
SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1)
+/**
+ * struct spi_delay - SPI delay information
+ * @value: Value for the delay
+ * @unit: Unit for the delay
+ */
+struct spi_delay {
+#define SPI_DELAY_UNIT_USECS 0
+#define SPI_DELAY_UNIT_NSECS 1
+#define SPI_DELAY_UNIT_SCK 2
+ u16 value;
+ u8 unit;
+};
+
+extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
+
/**
* struct spi_device - Controller side proxy for an SPI slave device
* @dev: Driver model representation of the device.
@@ -834,9 +849,6 @@ struct spi_transfer {
u16 delay_usecs;
u16 cs_change_delay;
u8 cs_change_delay_unit;
-#define SPI_DELAY_UNIT_USECS 0
-#define SPI_DELAY_UNIT_NSECS 1
-#define SPI_DELAY_UNIT_SCK 2
u32 speed_hz;
u16 word_delay;
--
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 v4 04/19] spi: sprd: convert transfer word delay to spi_delay struct
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
The Spreadtrum SPI driver is the only user of the `word_delay` field in
the `spi_transfer` struct.
This change converts the field to use the `spi_delay` struct. This also
enforces the users to specify the delay unit to be `SPI_DELAY_UNIT_SCK`.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-sprd.c | 15 ++++++++++++---
include/linux/spi/spi.h | 4 ++--
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c
index 1b7eebb72c07..7e72e8facff7 100644
--- a/drivers/spi/spi-sprd.c
+++ b/drivers/spi/spi-sprd.c
@@ -669,11 +669,15 @@ static void sprd_spi_set_speed(struct sprd_spi *ss, u32 speed_hz)
writel_relaxed(clk_div, ss->base + SPRD_SPI_CLKD);
}
-static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t)
+static int sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t)
{
+ struct spi_delay *d = &t->word_delay;
u16 word_delay, interval;
u32 val;
+ if (d->unit != SPI_DELAY_UNIT_SCK)
+ return -EINVAL;
+
val = readl_relaxed(ss->base + SPRD_SPI_CTL7);
val &= ~(SPRD_SPI_SCK_REV | SPRD_SPI_NG_TX | SPRD_SPI_NG_RX);
/* Set default chip selection, clock phase and clock polarity */
@@ -686,7 +690,7 @@ static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t)
* formula as below per datasheet:
* interval time (source clock cycles) = interval * 4 + 10.
*/
- word_delay = clamp_t(u16, t->word_delay, SPRD_SPI_MIN_DELAY_CYCLE,
+ word_delay = clamp_t(u16, d->value, SPRD_SPI_MIN_DELAY_CYCLE,
SPRD_SPI_MAX_DELAY_CYCLE);
interval = DIV_ROUND_UP(word_delay - 10, 4);
ss->word_delay = interval * 4 + 10;
@@ -711,6 +715,8 @@ static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t)
val &= ~SPRD_SPI_DATA_LINE2_EN;
writel_relaxed(val, ss->base + SPRD_SPI_CTL7);
+
+ return 0;
}
static int sprd_spi_setup_transfer(struct spi_device *sdev,
@@ -719,13 +725,16 @@ static int sprd_spi_setup_transfer(struct spi_device *sdev,
struct sprd_spi *ss = spi_controller_get_devdata(sdev->controller);
u8 bits_per_word = t->bits_per_word;
u32 val, mode = 0;
+ int ret;
ss->len = t->len;
ss->tx_buf = t->tx_buf;
ss->rx_buf = t->rx_buf;
ss->hw_mode = sdev->mode;
- sprd_spi_init_hw(ss, t);
+ ret = sprd_spi_init_hw(ss, t);
+ if (ret)
+ return ret;
/* Set tansfer speed and valid bits */
sprd_spi_set_speed(ss, t->speed_hz);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 9ded3f44d58e..16397b210e0c 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -759,7 +759,7 @@ extern void spi_res_release(struct spi_controller *ctlr,
* the next transfer or completing this @spi_message.
* @word_delay_usecs: microseconds to inter word delay after each word size
* (set by bits_per_word) transmission.
- * @word_delay: clock cycles to inter word delay after each word size
+ * @word_delay: inter word delay to be introduced after each word size
* (set by bits_per_word) transmission.
* @effective_speed_hz: the effective SCK-speed that was used to
* transfer this transfer. Set to 0 if the spi bus driver does
@@ -847,8 +847,8 @@ struct spi_transfer {
u8 word_delay_usecs;
u16 delay_usecs;
struct spi_delay cs_change_delay;
+ struct spi_delay word_delay;
u32 speed_hz;
- u16 word_delay;
u32 effective_speed_hz;
--
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 v4 05/19] spi: orion: use new `word_delay` field for SPI transfers
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
The `word_delay` field had it's type changed to `struct spi_delay`.
This allows users to specify nano-second or clock-cycle delays (if needed).
Converting to use `word_delay` is straightforward: it just uses the new
`spi_delay_exec()` routine, that handles the `unit` part.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-orion.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 6643ccdc2508..756c37e32dfe 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -467,8 +467,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
if (orion_spi_write_read_8bit(spi, &tx, &rx) < 0)
goto out;
count--;
- if (xfer->word_delay_usecs)
- udelay(xfer->word_delay_usecs);
+ spi_delay_exec(&xfer->word_delay, xfer);
} while (count);
} else if (word_len == 16) {
const u16 *tx = xfer->tx_buf;
@@ -478,8 +477,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
if (orion_spi_write_read_16bit(spi, &tx, &rx) < 0)
goto out;
count -= 2;
- if (xfer->word_delay_usecs)
- udelay(xfer->word_delay_usecs);
+ spi_delay_exec(&xfer->word_delay, xfer);
} while (count);
}
--
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 v4 06/19] spi: spidev: use new `word_delay` field for spi transfers
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
The `word_delay` field had it's type changed to `struct spi_delay`.
This allows users to specify nano-second or clock-cycle delays (if needed).
Converting to use `word_delay` is straightforward: it's just assigning the
value to `word_delay.value` and hard-coding the `word_delay.unit` to
`SPI_DELAY_UNIT_USECS`
This keeps the uapi for spidev un-changed. Changing it can be part of
another changeset and discussion.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spidev.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 255786f2e844..495319c2fa11 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -267,7 +267,8 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->bits_per_word = u_tmp->bits_per_word;
k_tmp->delay_usecs = u_tmp->delay_usecs;
k_tmp->speed_hz = u_tmp->speed_hz;
- k_tmp->word_delay_usecs = u_tmp->word_delay_usecs;
+ k_tmp->word_delay.value = u_tmp->word_delay_usecs;
+ k_tmp->word_delay.unit = SPI_DELAY_UNIT_USECS;
if (!k_tmp->speed_hz)
k_tmp->speed_hz = spidev->speed_hz;
#ifdef VERBOSE
--
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 v4 07/19] spi: core, atmel: convert `word_delay_usecs` -> `word_delay` for spi_device
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
This change does a conversion from the `word_delay_usecs` -> `word_delay`
for the `spi_device` struct.
This allows users to specify inter-word delays in other unit types
(nano-seconds or clock cycles), depending on how users want.
The Atmel SPI driver is the only current user of the `word_delay_usecs`
field (from the `spi_device` struct).
So, it needed a slight conversion to use the `word_delay` as an `spi_delay`
struct.
In SPI core, the only required mechanism is to update the `word_delay`
information per `spi_transfer`. This requires a bit more logic than before,
because it needs that both delays be converted to a common unit
(nano-seconds) for comparison.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-atmel.c | 26 ++++++++++++++++++++++++--
drivers/spi/spi.c | 24 ++++++++++++++++++++++--
include/linux/spi/spi.h | 7 ++-----
3 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index f00b367523cd..f04b92791515 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1172,12 +1172,31 @@ atmel_spi_pdc_interrupt(int irq, void *dev_id)
return ret;
}
+static int atmel_word_delay_csr(struct spi_device *spi, struct atmel_spi *as)
+{
+ struct spi_delay *delay = &spi->word_delay;
+ u32 value = delay->value;
+
+ switch (delay->unit) {
+ case SPI_DELAY_UNIT_NSECS:
+ value /= 1000;
+ break;
+ case SPI_DELAY_UNIT_USECS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (as->spi_clk / 1000000 * value) >> 5;
+}
+
static int atmel_spi_setup(struct spi_device *spi)
{
struct atmel_spi *as;
struct atmel_spi_device *asd;
u32 csr;
unsigned int bits = spi->bits_per_word;
+ int word_delay_csr;
as = spi_master_get_devdata(spi->master);
@@ -1201,11 +1220,14 @@ static int atmel_spi_setup(struct spi_device *spi)
*/
csr |= SPI_BF(DLYBS, 0);
+ word_delay_csr = atmel_word_delay_csr(spi, as);
+ if (word_delay_csr < 0)
+ return word_delay_csr;
+
/* DLYBCT adds delays between words. This is useful for slow devices
* that need a bit of time to setup the next transfer.
*/
- csr |= SPI_BF(DLYBCT,
- (as->spi_clk / 1000000 * spi->word_delay_usecs) >> 5);
+ csr |= SPI_BF(DLYBCT, word_delay_csr);
asd = spi->controller_state;
if (!asd) {
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d0bf0ffca042..ba8731b77753 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3159,6 +3159,26 @@ void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold,
}
EXPORT_SYMBOL_GPL(spi_set_cs_timing);
+static int _spi_xfer_word_delay_update(struct spi_transfer *xfer,
+ struct spi_device *spi)
+{
+ int delay1, delay2;
+
+ delay1 = _spi_delay_to_ns(&xfer->word_delay, xfer);
+ if (delay1 < 0)
+ return delay1;
+
+ delay2 = _spi_delay_to_ns(&spi->word_delay, xfer);
+ if (delay2 < 0)
+ return delay2;
+
+ if (delay1 < delay2)
+ memcpy(&xfer->word_delay, &spi->word_delay,
+ sizeof(xfer->word_delay));
+
+ return 0;
+}
+
static int __spi_validate(struct spi_device *spi, struct spi_message *message)
{
struct spi_controller *ctlr = spi->controller;
@@ -3294,8 +3314,8 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
return -EINVAL;
}
- if (xfer->word_delay_usecs < spi->word_delay_usecs)
- xfer->word_delay_usecs = spi->word_delay_usecs;
+ if (_spi_xfer_word_delay_update(xfer, spi))
+ return -EINVAL;
}
message->status = -EINPROGRESS;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 16397b210e0c..e7ec0be16a82 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -138,7 +138,7 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
* the spi_master.
* @cs_gpiod: gpio descriptor of the chipselect line (optional, NULL when
* not using a GPIO line)
- * @word_delay_usecs: microsecond delay to be inserted between consecutive
+ * @word_delay: delay to be inserted between consecutive
* words of a transfer
*
* @statistics: statistics for the spi_device
@@ -188,7 +188,7 @@ struct spi_device {
const char *driver_override;
int cs_gpio; /* LEGACY: chip select gpio */
struct gpio_desc *cs_gpiod; /* chip select gpio desc */
- uint8_t word_delay_usecs; /* inter-word delay */
+ struct spi_delay word_delay; /* inter-word delay */
/* the statistics */
struct spi_statistics statistics;
@@ -757,8 +757,6 @@ extern void spi_res_release(struct spi_controller *ctlr,
* @delay_usecs: microseconds to delay after this transfer before
* (optionally) changing the chipselect status, then starting
* the next transfer or completing this @spi_message.
- * @word_delay_usecs: microseconds to inter word delay after each word size
- * (set by bits_per_word) transmission.
* @word_delay: inter word delay to be introduced after each word size
* (set by bits_per_word) transmission.
* @effective_speed_hz: the effective SCK-speed that was used to
@@ -844,7 +842,6 @@ struct spi_transfer {
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
- u8 word_delay_usecs;
u16 delay_usecs;
struct spi_delay cs_change_delay;
struct spi_delay word_delay;
--
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 v4 09/19] spi: use new `spi_transfer_delay_exec` helper where straightforward
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
For many places in the spi drivers, using the new `spi_transfer_delay`
helper is straightforward.
It's just replacing:
```
if (t->delay_usecs)
udelay(t->delay_usecs);
```
with `spi_transfer_delay(t)` which handles both `delay_usecs` and the new
`delay` field.
This change replaces in all places (in the spi drivers) where this change
is simple.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-atmel.c | 3 +--
drivers/spi/spi-bcm63xx-hsspi.c | 3 +--
drivers/spi/spi-cavium.c | 3 +--
drivers/spi/spi-fsl-dspi.c | 3 +--
drivers/spi/spi-fsl-espi.c | 3 +--
drivers/spi/spi-fsl-spi.c | 3 +--
drivers/spi/spi-mpc512x-psc.c | 3 +--
drivers/spi/spi-mpc52xx-psc.c | 3 +--
drivers/spi/spi-omap-100k.c | 3 +--
drivers/spi/spi-pl022.c | 25 +++++++++++--------------
drivers/spi/spi-sc18is602.c | 3 +--
drivers/spi/spi-sh-hspi.c | 3 +--
drivers/spi/spi-topcliff-pch.c | 7 +------
drivers/spi/spi-txx9.c | 3 +--
drivers/spi/spi-xcomm.c | 3 +--
15 files changed, 25 insertions(+), 46 deletions(-)
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index f04b92791515..2f7388e53b82 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1389,8 +1389,7 @@ static int atmel_spi_one_transfer(struct spi_master *master,
&& as->use_pdc)
atmel_spi_dma_unmap_xfer(master, xfer);
- if (xfer->delay_usecs)
- udelay(xfer->delay_usecs);
+ spi_transfer_delay_exec(xfer);
if (xfer->cs_change) {
if (list_is_last(&xfer->transfer_list,
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 9a06ffdb73b8..949b59b6449f 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -291,8 +291,7 @@ static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
msg->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
if (t->cs_change)
bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
diff --git a/drivers/spi/spi-cavium.c b/drivers/spi/spi-cavium.c
index 5aaf21582cb5..6854c3ce423b 100644
--- a/drivers/spi/spi-cavium.c
+++ b/drivers/spi/spi-cavium.c
@@ -119,8 +119,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
*rx_buf++ = (u8)v;
}
- if (xfer->delay_usecs)
- udelay(xfer->delay_usecs);
+ spi_transfer_delay_exec(xfer);
return xfer->len;
}
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 53335ccc98f6..736071e2b9d8 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -752,8 +752,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
dspi->waitflags = 0;
}
- if (transfer->delay_usecs)
- udelay(transfer->delay_usecs);
+ spi_transfer_delay_exec(transfer);
}
out:
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index f20326714b9d..732bf3195917 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -427,8 +427,7 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans)
ret = fsl_espi_bufs(spi, trans);
- if (trans->delay_usecs)
- udelay(trans->delay_usecs);
+ spi_transfer_delay_exec(trans);
return ret;
}
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index 1d9b33aa1a3b..139ab57aec99 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -417,8 +417,7 @@ static int fsl_spi_do_one_msg(struct spi_master *master,
}
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
if (cs_change) {
ndelay(nsecs);
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index a337b842ae8c..ea1b07953d38 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -311,8 +311,7 @@ static int mpc512x_psc_spi_msg_xfer(struct spi_master *master,
break;
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
if (cs_change)
mpc512x_psc_spi_deactivate_cs(spi);
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index c7e478b9b586..17935e71b02f 100644
--- a/drivers/spi/spi-mpc52xx-psc.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
@@ -234,8 +234,7 @@ static void mpc52xx_psc_spi_work(struct work_struct *work)
break;
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
if (cs_change)
mpc52xx_psc_spi_deactivate_cs(spi);
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c
index b955ca8796d2..89608eb55f1f 100644
--- a/drivers/spi/spi-omap-100k.c
+++ b/drivers/spi/spi-omap-100k.c
@@ -321,8 +321,7 @@ static int omap1_spi100k_transfer_one_message(struct spi_master *master,
}
}
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
/* ignore the "leave it on after last xfer" hint */
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 7fedea67159c..3024c30e7f2e 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -485,12 +485,11 @@ static void giveback(struct pl022 *pl022)
struct spi_transfer, transfer_list);
/* Delay if requested before any change in chip select */
- if (last_transfer->delay_usecs)
- /*
- * FIXME: This runs in interrupt context.
- * Is this really smart?
- */
- udelay(last_transfer->delay_usecs);
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ spi_transfer_delay_exec(last_transfer);
if (!last_transfer->cs_change) {
struct spi_message *next_msg;
@@ -1401,12 +1400,11 @@ static void pump_transfers(unsigned long data)
previous = list_entry(transfer->transfer_list.prev,
struct spi_transfer,
transfer_list);
- if (previous->delay_usecs)
- /*
- * FIXME: This runs in interrupt context.
- * Is this really smart?
- */
- udelay(previous->delay_usecs);
+ /*
+ * FIXME: This runs in interrupt context.
+ * Is this really smart?
+ */
+ spi_transfer_delay_exec(previous);
/* Reselect chip select only if cs_change was requested */
if (previous->cs_change)
@@ -1520,8 +1518,7 @@ static void do_polling_transfer(struct pl022 *pl022)
previous =
list_entry(transfer->transfer_list.prev,
struct spi_transfer, transfer_list);
- if (previous->delay_usecs)
- udelay(previous->delay_usecs);
+ spi_transfer_delay_exec(previous);
if (previous->cs_change)
pl022_cs_control(pl022, SSP_CHIP_SELECT);
} else {
diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c
index 11acddc83304..5497eeb3bf3e 100644
--- a/drivers/spi/spi-sc18is602.c
+++ b/drivers/spi/spi-sc18is602.c
@@ -211,8 +211,7 @@ static int sc18is602_transfer_one(struct spi_master *master,
}
status = 0;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
}
m->status = status;
spi_finalize_current_message(master);
diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c
index 7f73f91d412a..a62034e2a7cb 100644
--- a/drivers/spi/spi-sh-hspi.c
+++ b/drivers/spi/spi-sh-hspi.c
@@ -190,8 +190,7 @@ static int hspi_transfer_one_message(struct spi_controller *ctlr,
msg->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
if (cs_change) {
ndelay(nsecs);
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index f88cbb94ce12..223353fa2d8a 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -1229,12 +1229,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
"%s:data->current_msg->actual_length=%d\n",
__func__, data->current_msg->actual_length);
- /* check for delay */
- if (data->cur_trans->delay_usecs) {
- dev_dbg(&data->master->dev, "%s:delay in usec=%d\n",
- __func__, data->cur_trans->delay_usecs);
- udelay(data->cur_trans->delay_usecs);
- }
+ spi_transfer_delay_exec(data->cur_trans);
spin_lock(&data->lock);
diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c
index 51759d3fd45f..83daaa597acc 100644
--- a/drivers/spi/spi-txx9.c
+++ b/drivers/spi/spi-txx9.c
@@ -248,8 +248,7 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m)
len -= count * wsize;
}
m->actual_length += t->len;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
if (!cs_change)
continue;
diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c
index a3496c46cc1b..1d9b3f03d986 100644
--- a/drivers/spi/spi-xcomm.c
+++ b/drivers/spi/spi-xcomm.c
@@ -188,8 +188,7 @@ static int spi_xcomm_transfer_one(struct spi_master *master,
}
status = 0;
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ spi_transfer_delay_exec(t);
is_first = false;
}
--
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 v4 08/19] spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec()
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
The change introduces the `delay` field to the `spi_transfer` struct as an
`struct spi_delay` type.
This intends to eventually replace `delay_usecs`.
But, since there are many users of `delay_usecs`, this needs some
intermediate work.
A helper called `spi_transfer_delay_exec()` is also added, which maintains
backwards compatibility with `delay_usecs`, by assigning the value to
`delay` if non-zero.
This should maintain backwards compatibility with current users of
`udelay_usecs`.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi.c | 6 +++---
include/linux/spi/spi.h | 18 ++++++++++++++++++
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ba8731b77753..489eb64d9ee0 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1238,8 +1238,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
if (msg->status != -EINPROGRESS)
goto out;
- if (xfer->delay_usecs)
- _spi_transfer_delay_ns(xfer->delay_usecs * 1000);
+ spi_transfer_delay_exec(xfer);
if (xfer->cs_change) {
if (list_is_last(&xfer->transfer_list,
@@ -2904,10 +2903,11 @@ struct spi_replaced_transfers *spi_replace_transfers(
/* add to list */
list_add(&xfer->transfer_list, rxfer->replaced_after);
- /* clear cs_change and delay_usecs for all but the last */
+ /* clear cs_change and delay for all but the last */
if (i) {
xfer->cs_change = false;
xfer->delay_usecs = 0;
+ xfer->delay.value = 0;
}
}
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index e7ec0be16a82..2cee9ac35737 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -754,6 +754,9 @@ extern void spi_res_release(struct spi_controller *ctlr,
* @cs_change: affects chipselect after this transfer completes
* @cs_change_delay: delay between cs deassert and assert when
* @cs_change is set and @spi_transfer is not the last in @spi_message
+ * @delay: delay to be introduced after this transfer before
+ * (optionally) changing the chipselect status, then starting
+ * the next transfer or completing this @spi_message.
* @delay_usecs: microseconds to delay after this transfer before
* (optionally) changing the chipselect status, then starting
* the next transfer or completing this @spi_message.
@@ -843,6 +846,7 @@ struct spi_transfer {
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
+ struct spi_delay delay;
struct spi_delay cs_change_delay;
struct spi_delay word_delay;
u32 speed_hz;
@@ -942,6 +946,20 @@ spi_transfer_del(struct spi_transfer *t)
list_del(&t->transfer_list);
}
+static inline int
+spi_transfer_delay_exec(struct spi_transfer *t)
+{
+ struct spi_delay d;
+
+ if (t->delay_usecs) {
+ d.value = t->delay_usecs;
+ d.unit = SPI_DELAY_UNIT_USECS;
+ return spi_delay_exec(&d, NULL);
+ }
+
+ return spi_delay_exec(&t->delay, t);
+}
+
/**
* spi_message_init_with_transfers - Initialize spi_message and append transfers
* @m: spi_message to be initialized
--
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 v4 10/19] spi: tegra114: use `spi_transfer_delay_exec` helper
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
The tegra114 driver has a weird/separate `tegra_spi_transfer_delay()`
function that does 2 delays: one mdelay() and one udelay().
This was introduced via commit f4fade12d506e14867a2b0a5e2f7aaf227297d8b
("spi/tegra114: Correct support for cs_change").
There doesn't seem to be a mention in that commit message to suggest a
specific need/use-case for having the 2 delay calls.
For the most part, udelay() should be sufficient.
This change replaces it with the new `spi_transfer_delay_exec()`, which
should do the same thing.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-tegra114.c | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 39374c2edcf3..8133dc49d34f 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -984,17 +984,6 @@ static int tegra_spi_setup(struct spi_device *spi)
return 0;
}
-static void tegra_spi_transfer_delay(int delay)
-{
- if (!delay)
- return;
-
- if (delay >= 1000)
- mdelay(delay / 1000);
-
- udelay(delay % 1000);
-}
-
static void tegra_spi_transfer_end(struct spi_device *spi)
{
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
@@ -1098,7 +1087,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
complete_xfer:
if (ret < 0 || skip) {
tegra_spi_transfer_end(spi);
- tegra_spi_transfer_delay(xfer->delay_usecs);
+ spi_transfer_delay_exec(xfer);
goto exit;
} else if (list_is_last(&xfer->transfer_list,
&msg->transfers)) {
@@ -1106,11 +1095,11 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
tspi->cs_control = spi;
else {
tegra_spi_transfer_end(spi);
- tegra_spi_transfer_delay(xfer->delay_usecs);
+ spi_transfer_delay_exec(xfer);
}
} else if (xfer->cs_change) {
tegra_spi_transfer_end(spi);
- tegra_spi_transfer_delay(xfer->delay_usecs);
+ spi_transfer_delay_exec(xfer);
}
}
--
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 v4 11/19] spi: tegra20-sflash: use to new `spi_transfer_delay_exec`
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
This conversion to the spi_transfer_delay_exec() helper is not
straightforward, as it seems that when a delay is present, the controller
issues a command, and then a delay is followed.
This change adds support for the new `delay` field which is of type
`spi_delay` and keeps backwards compatibility with the old `delay_usecs`
field.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-tegra20-sflash.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c
index cd714a4f52c6..ff1933e55830 100644
--- a/drivers/spi/spi-tegra20-sflash.c
+++ b/drivers/spi/spi-tegra20-sflash.c
@@ -341,10 +341,11 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master,
goto exit;
}
msg->actual_length += xfer->len;
- if (xfer->cs_change && xfer->delay_usecs) {
+ if (xfer->cs_change &&
+ (xfer->delay_usecs || xfer->delay.value)) {
tegra_sflash_writel(tsd, tsd->def_command_reg,
SPI_COMMAND);
- udelay(xfer->delay_usecs);
+ spi_transfer_delay_exec(xfer);
}
}
ret = 0;
--
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 v4 12/19] spi: spi-loopback-test: use new `delay` field
From: Alexandru Ardelean @ 2019-09-26 10:51 UTC (permalink / raw)
To: bcm-kernel-feedback-list, linux-iio, linux-kernel, linux-spi,
linux-arm-kernel, linux-tegra
Cc: f.fainelli, baolin.wang, zhang.lyra, linus.walleij, broonie,
orsonzhai, Alexandru Ardelean, jic23
In-Reply-To: <20190926105147.7839-1-alexandru.ardelean@analog.com>
This change replaces the use of the `delay_usecs` field with the new
`delay` field. The code/test still uses micro-seconds, but they are now
configured and used via the `struct spi_delay` format of the `delay` field.
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
drivers/spi/spi-loopback-test.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c
index 6f18d4952767..b6d79cd156fb 100644
--- a/drivers/spi/spi-loopback-test.c
+++ b/drivers/spi/spi-loopback-test.c
@@ -298,12 +298,18 @@ static struct spi_test spi_tests[] = {
{
.tx_buf = TX(0),
.rx_buf = RX(0),
- .delay_usecs = 1000,
+ .delay = {
+ .value = 1000,
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
},
{
.tx_buf = TX(0),
.rx_buf = RX(0),
- .delay_usecs = 1000,
+ .delay = {
+ .value = 1000,
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
},
},
},
@@ -537,7 +543,7 @@ static int spi_test_check_elapsed_time(struct spi_device *spi,
unsigned long long nbits = (unsigned long long)BITS_PER_BYTE *
xfer->len;
- delay_usecs += xfer->delay_usecs;
+ delay_usecs += xfer->delay.value;
if (!xfer->speed_hz)
continue;
estimated_time += div_u64(nbits * NSEC_PER_SEC, xfer->speed_hz);
--
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
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