From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 1B965CA0EFF for ; Fri, 22 Aug 2025 15:13:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:References:In-Reply-To:Cc:To:From: Subject:MIME-Version:Date:Message-ID:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5rXceGbkHRlatqfKEsk+TLDOn7b+Z49Ktk73AA8qkjM=; b=V/7Gji7DGJECOH EZaW7zd+UvoTwP6oDbZAnxiU/xClh+927B6h4iH8TRPzavIoKPfPA4Zy47bH8e45ZS1Iy880bQzbF AGc97HG7Qvq5shBbKDoO5Unl6xDQONCjquGi1ASUAz1rn8u8zBNR/FXZhl2W6CwQ750OVWVsd0vG6 VCOhwHd6z19OeSDSBn6SeTRoIdOd508odqlYKzF3xuVOXz9Eq1g0WthrgEDLVSrIVx16YnG1pIyhd TeAO1DReB/F2SNe920xIPVf82B1HeNqxc3pTJXrnXznZMbXkymrduMNG9RiemMcYwCF3APlIohjRq f41DoSZKjDC9y0tmUbWA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1upTRm-00000002y3S-066c; Fri, 22 Aug 2025 15:12:54 +0000 Received: from mailout2.w1.samsung.com ([210.118.77.12]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1upOyN-00000002C2P-3OYF for linux-riscv@lists.infradead.org; Fri, 22 Aug 2025 10:26:17 +0000 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250822102611euoutp02a65ced992fbd36b183fb8ed2af9c73a1~eD_KzysGD1798517985euoutp02k for ; Fri, 22 Aug 2025 10:26:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250822102611euoutp02a65ced992fbd36b183fb8ed2af9c73a1~eD_KzysGD1798517985euoutp02k DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1755858371; bh=Q+6Xs7HEwbU8PjeuZpGHjXULFml+y8xhITqWaoDVDIs=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=QZDEdZDAnccRvEuBv0P/VUu+KDFDgpIarhA+x135y0cdj3BmsFQJRLtKgXDL+MwzF /WYi2nWdW6zL/0Roxg+DLl1VWVU95hsWEsRlTh9BOyAYJr86Q8x6wD5KCWk7qPQxzX sohnq3aQMQJLk1Phr4N9piC8RZObVrLVhFTOD6bI= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250822102610eucas1p17b57ad56569020fb2d563c61d4fd5a4a~eD_KBaM3q2024620246eucas1p1c; Fri, 22 Aug 2025 10:26:10 +0000 (GMT) Received: from [192.168.1.44] (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250822102609eusmtip10d4a23b2bd09752681a16ed23cc33ccf~eD_I7t1y21166311663eusmtip1h; Fri, 22 Aug 2025 10:26:09 +0000 (GMT) Message-ID: Date: Fri, 22 Aug 2025 12:26:08 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v13 1/4] drm/imagination: Use pwrseq for TH1520 GPU power management From: Michal Wilczynski To: Matt Coster , Drew Fustini Cc: Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bartosz Golaszewski , Philipp Zabel , Frank Binns , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Ulf Hansson , Marek Szyprowski , Drew Fustini , "linux-riscv@lists.infradead.org" , "devicetree@vger.kernel.org" , "linux-kernel@vger.kernel.org" , "linux-pm@vger.kernel.org" , "dri-devel@lists.freedesktop.org" Content-Language: en-US In-Reply-To: <55e606c5-9ac0-4e0b-8506-5f88a6fc540e@samsung.com> X-CMS-MailID: 20250822102610eucas1p17b57ad56569020fb2d563c61d4fd5a4a X-Msg-Generator: CA X-RootMTR: 20250821222020eucas1p20e40b85b991da0b4d867df76e55350ed X-EPHeader: CA X-CMS-RootMailID: 20250821222020eucas1p20e40b85b991da0b4d867df76e55350ed References: <20250822-apr_14_for_sending-v13-0-af656f7cc6c3@samsung.com> <20250822-apr_14_for_sending-v13-1-af656f7cc6c3@samsung.com> <55e606c5-9ac0-4e0b-8506-5f88a6fc540e@samsung.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250822_032616_200618_F99EEA4E X-CRM114-Status: GOOD ( 28.96 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org On 8/22/25 12:04, Michal Wilczynski wrote: > > > On 8/22/25 11:28, Matt Coster wrote: >> On 21/08/2025 23:20, Michal Wilczynski wrote: >>> Update the Imagination PVR DRM driver to leverage the pwrseq framework >>> for managing the complex power sequence of the GPU on the T-HEAD TH1520 >>> SoC. >>> >>> To cleanly separate platform-specific logic from the generic driver, >>> this patch introduces an `init` callback to the `pwr_power_sequence_ops` >>> struct. This allows for different power management strategies to be >>> selected at probe time based on the device's compatible string. >>> >>> A `pvr_device_data` struct, associated with each compatible in the >>> of_device_id table, points to the appropriate ops table (manual or >>> pwrseq). >>> >>> At probe time, the driver now calls the `->init()` op. For pwrseq-based >>> platforms, this callback calls `devm_pwrseq_get("gpu-power")`, deferring >>> probe if the sequencer is not yet available. For other platforms, it >>> falls back to the existing manual clock and reset handling. The runtime >>> PM callbacks continue to call the appropriate functions via the ops >>> table. >>> >>> Signed-off-by: Michal Wilczynski >> >> Reviewed-by: Matt Coster >> >> Would you like me to take the non-DTS changes via drm-misc-next? > > Yeah I think this would be appropriate. > Thanks ! Hi Drew, Matt offered to take the non-DTS patches (1/4 and 4/4) from this series through the DRM tree. This leaves the DT binding and TH1520 DT patches (2/4 and 3/4). Would you be able to pick them up through your tree ? Thanks ! > >> >> Cheers, >> Matt >> >>> --- >>> drivers/gpu/drm/imagination/pvr_device.c | 22 +---- >>> drivers/gpu/drm/imagination/pvr_device.h | 17 ++++ >>> drivers/gpu/drm/imagination/pvr_drv.c | 23 ++++- >>> drivers/gpu/drm/imagination/pvr_power.c | 158 +++++++++++++++++++++++-------- >>> drivers/gpu/drm/imagination/pvr_power.h | 15 +++ >>> 5 files changed, 176 insertions(+), 59 deletions(-) >>> >>> diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c >>> index 8b9ba4983c4cb5bc40342fcafc4259078bc70547..294b6019b4155bb7fdb7de73ccf7fa8ad867811f 100644 >>> --- a/drivers/gpu/drm/imagination/pvr_device.c >>> +++ b/drivers/gpu/drm/imagination/pvr_device.c >>> @@ -23,6 +23,7 @@ >>> #include >>> #include >>> #include >>> +#include >>> #include >>> #include >>> #include >>> @@ -121,21 +122,6 @@ static int pvr_device_clk_init(struct pvr_device *pvr_dev) >>> return 0; >>> } >>> >>> -static int pvr_device_reset_init(struct pvr_device *pvr_dev) >>> -{ >>> - struct drm_device *drm_dev = from_pvr_device(pvr_dev); >>> - struct reset_control *reset; >>> - >>> - reset = devm_reset_control_get_optional_exclusive(drm_dev->dev, NULL); >>> - if (IS_ERR(reset)) >>> - return dev_err_probe(drm_dev->dev, PTR_ERR(reset), >>> - "failed to get gpu reset line\n"); >>> - >>> - pvr_dev->reset = reset; >>> - >>> - return 0; >>> -} >>> - >>> /** >>> * pvr_device_process_active_queues() - Process all queue related events. >>> * @pvr_dev: PowerVR device to check >>> @@ -618,6 +604,9 @@ pvr_device_init(struct pvr_device *pvr_dev) >>> struct device *dev = drm_dev->dev; >>> int err; >>> >>> + /* Get the platform-specific data based on the compatible string. */ >>> + pvr_dev->device_data = of_device_get_match_data(dev); >>> + >>> /* >>> * Setup device parameters. We do this first in case other steps >>> * depend on them. >>> @@ -631,8 +620,7 @@ pvr_device_init(struct pvr_device *pvr_dev) >>> if (err) >>> return err; >>> >>> - /* Get the reset line for the GPU */ >>> - err = pvr_device_reset_init(pvr_dev); >>> + err = pvr_dev->device_data->pwr_ops->init(pvr_dev); >>> if (err) >>> return err; >>> >>> diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/imagination/pvr_device.h >>> index 7cb01c38d2a9c3fc71effe789d4dfe54eddd93ee..ab8f56ae15df6c2888feb16b1d87b59510961936 100644 >>> --- a/drivers/gpu/drm/imagination/pvr_device.h >>> +++ b/drivers/gpu/drm/imagination/pvr_device.h >>> @@ -37,6 +37,9 @@ struct clk; >>> /* Forward declaration from . */ >>> struct firmware; >>> >>> +/* Forward declaration from */ >>> +struct pwrseq_desc; >>> + >>> /** >>> * struct pvr_gpu_id - Hardware GPU ID information for a PowerVR device >>> * @b: Branch ID. >>> @@ -57,6 +60,14 @@ struct pvr_fw_version { >>> u16 major, minor; >>> }; >>> >>> +/** >>> + * struct pvr_device_data - Platform specific data associated with a compatible string. >>> + * @pwr_ops: Pointer to a structure with platform-specific power functions. >>> + */ >>> +struct pvr_device_data { >>> + const struct pvr_power_sequence_ops *pwr_ops; >>> +}; >>> + >>> /** >>> * struct pvr_device - powervr-specific wrapper for &struct drm_device >>> */ >>> @@ -98,6 +109,9 @@ struct pvr_device { >>> /** @fw_version: Firmware version detected at runtime. */ >>> struct pvr_fw_version fw_version; >>> >>> + /** @device_data: Pointer to platform-specific data. */ >>> + const struct pvr_device_data *device_data; >>> + >>> /** @regs_resource: Resource representing device control registers. */ >>> struct resource *regs_resource; >>> >>> @@ -148,6 +162,9 @@ struct pvr_device { >>> */ >>> struct reset_control *reset; >>> >>> + /** @pwrseq: Pointer to a power sequencer, if one is used. */ >>> + struct pwrseq_desc *pwrseq; >>> + >>> /** @irq: IRQ number. */ >>> int irq; >>> >>> diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c >>> index b058ec183bb30ab5c3db17ebaadf2754520a2a1f..916b40ced7eb0408fe985ba1b83b3be2eb024bae 100644 >>> --- a/drivers/gpu/drm/imagination/pvr_drv.c >>> +++ b/drivers/gpu/drm/imagination/pvr_drv.c >>> @@ -1480,15 +1480,33 @@ static void pvr_remove(struct platform_device *plat_dev) >>> pvr_power_domains_fini(pvr_dev); >>> } >>> >>> +static const struct pvr_device_data pvr_device_data_manual = { >>> + .pwr_ops = &pvr_power_sequence_ops_manual, >>> +}; >>> + >>> +static const struct pvr_device_data pvr_device_data_pwrseq = { >>> + .pwr_ops = &pvr_power_sequence_ops_pwrseq, >>> +}; >>> + >>> static const struct of_device_id dt_match[] = { >>> - { .compatible = "img,img-rogue", .data = NULL }, >>> + { >>> + .compatible = "thead,th1520-gpu", >>> + .data = &pvr_device_data_pwrseq, >>> + }, >>> + { >>> + .compatible = "img,img-rogue", >>> + .data = &pvr_device_data_manual, >>> + }, >>> >>> /* >>> * This legacy compatible string was introduced early on before the more generic >>> * "img,img-rogue" was added. Keep it around here for compatibility, but never use >>> * "img,img-axe" in new devicetrees. >>> */ >>> - { .compatible = "img,img-axe", .data = NULL }, >>> + { >>> + .compatible = "img,img-axe", >>> + .data = &pvr_device_data_manual, >>> + }, >>> {} >>> }; >>> MODULE_DEVICE_TABLE(of, dt_match); >>> @@ -1513,4 +1531,5 @@ MODULE_DESCRIPTION(PVR_DRIVER_DESC); >>> MODULE_LICENSE("Dual MIT/GPL"); >>> MODULE_IMPORT_NS("DMA_BUF"); >>> MODULE_FIRMWARE("powervr/rogue_33.15.11.3_v1.fw"); >>> +MODULE_FIRMWARE("powervr/rogue_36.52.104.182_v1.fw"); >>> MODULE_FIRMWARE("powervr/rogue_36.53.104.796_v1.fw"); >>> diff --git a/drivers/gpu/drm/imagination/pvr_power.c b/drivers/gpu/drm/imagination/pvr_power.c >>> index 187a07e0bd9adb2f0713ac2c8e091229f4027354..c6e7ff9e935d3b348eff6953c633c72410fdf507 100644 >>> --- a/drivers/gpu/drm/imagination/pvr_power.c >>> +++ b/drivers/gpu/drm/imagination/pvr_power.c >>> @@ -18,6 +18,7 @@ >>> #include >>> #include >>> #include >>> +#include >>> #include >>> #include >>> #include >>> @@ -234,6 +235,118 @@ pvr_watchdog_init(struct pvr_device *pvr_dev) >>> return 0; >>> } >>> >>> +static int pvr_power_init_manual(struct pvr_device *pvr_dev) >>> +{ >>> + struct drm_device *drm_dev = from_pvr_device(pvr_dev); >>> + struct reset_control *reset; >>> + >>> + reset = devm_reset_control_get_optional_exclusive(drm_dev->dev, NULL); >>> + if (IS_ERR(reset)) >>> + return dev_err_probe(drm_dev->dev, PTR_ERR(reset), >>> + "failed to get gpu reset line\n"); >>> + >>> + pvr_dev->reset = reset; >>> + >>> + return 0; >>> +} >>> + >>> +static int pvr_power_on_sequence_manual(struct pvr_device *pvr_dev) >>> +{ >>> + int err; >>> + >>> + err = clk_prepare_enable(pvr_dev->core_clk); >>> + if (err) >>> + return err; >>> + >>> + err = clk_prepare_enable(pvr_dev->sys_clk); >>> + if (err) >>> + goto err_core_clk_disable; >>> + >>> + err = clk_prepare_enable(pvr_dev->mem_clk); >>> + if (err) >>> + goto err_sys_clk_disable; >>> + >>> + /* >>> + * According to the hardware manual, a delay of at least 32 clock >>> + * cycles is required between de-asserting the clkgen reset and >>> + * de-asserting the GPU reset. Assuming a worst-case scenario with >>> + * a very high GPU clock frequency, a delay of 1 microsecond is >>> + * sufficient to ensure this requirement is met across all >>> + * feasible GPU clock speeds. >>> + */ >>> + udelay(1); >>> + >>> + err = reset_control_deassert(pvr_dev->reset); >>> + if (err) >>> + goto err_mem_clk_disable; >>> + >>> + return 0; >>> + >>> +err_mem_clk_disable: >>> + clk_disable_unprepare(pvr_dev->mem_clk); >>> + >>> +err_sys_clk_disable: >>> + clk_disable_unprepare(pvr_dev->sys_clk); >>> + >>> +err_core_clk_disable: >>> + clk_disable_unprepare(pvr_dev->core_clk); >>> + >>> + return err; >>> +} >>> + >>> +static int pvr_power_off_sequence_manual(struct pvr_device *pvr_dev) >>> +{ >>> + int err; >>> + >>> + err = reset_control_assert(pvr_dev->reset); >>> + >>> + clk_disable_unprepare(pvr_dev->mem_clk); >>> + clk_disable_unprepare(pvr_dev->sys_clk); >>> + clk_disable_unprepare(pvr_dev->core_clk); >>> + >>> + return err; >>> +} >>> + >>> +const struct pvr_power_sequence_ops pvr_power_sequence_ops_manual = { >>> + .init = pvr_power_init_manual, >>> + .power_on = pvr_power_on_sequence_manual, >>> + .power_off = pvr_power_off_sequence_manual, >>> +}; >>> + >>> +static int pvr_power_init_pwrseq(struct pvr_device *pvr_dev) >>> +{ >>> + struct device *dev = from_pvr_device(pvr_dev)->dev; >>> + >>> + pvr_dev->pwrseq = devm_pwrseq_get(dev, "gpu-power"); >>> + if (IS_ERR(pvr_dev->pwrseq)) { >>> + /* >>> + * This platform requires a sequencer. If we can't get it, we >>> + * must return the error (including -EPROBE_DEFER to wait for >>> + * the provider to appear) >>> + */ >>> + return dev_err_probe(dev, PTR_ERR(pvr_dev->pwrseq), >>> + "Failed to get required power sequencer\n"); >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int pvr_power_on_sequence_pwrseq(struct pvr_device *pvr_dev) >>> +{ >>> + return pwrseq_power_on(pvr_dev->pwrseq); >>> +} >>> + >>> +static int pvr_power_off_sequence_pwrseq(struct pvr_device *pvr_dev) >>> +{ >>> + return pwrseq_power_off(pvr_dev->pwrseq); >>> +} >>> + >>> +const struct pvr_power_sequence_ops pvr_power_sequence_ops_pwrseq = { >>> + .init = pvr_power_init_pwrseq, >>> + .power_on = pvr_power_on_sequence_pwrseq, >>> + .power_off = pvr_power_off_sequence_pwrseq, >>> +}; >>> + >>> int >>> pvr_power_device_suspend(struct device *dev) >>> { >>> @@ -252,11 +365,7 @@ pvr_power_device_suspend(struct device *dev) >>> goto err_drm_dev_exit; >>> } >>> >>> - clk_disable_unprepare(pvr_dev->mem_clk); >>> - clk_disable_unprepare(pvr_dev->sys_clk); >>> - clk_disable_unprepare(pvr_dev->core_clk); >>> - >>> - err = reset_control_assert(pvr_dev->reset); >>> + err = pvr_dev->device_data->pwr_ops->power_off(pvr_dev); >>> >>> err_drm_dev_exit: >>> drm_dev_exit(idx); >>> @@ -276,53 +385,22 @@ pvr_power_device_resume(struct device *dev) >>> if (!drm_dev_enter(drm_dev, &idx)) >>> return -EIO; >>> >>> - err = clk_prepare_enable(pvr_dev->core_clk); >>> + err = pvr_dev->device_data->pwr_ops->power_on(pvr_dev); >>> if (err) >>> goto err_drm_dev_exit; >>> >>> - err = clk_prepare_enable(pvr_dev->sys_clk); >>> - if (err) >>> - goto err_core_clk_disable; >>> - >>> - err = clk_prepare_enable(pvr_dev->mem_clk); >>> - if (err) >>> - goto err_sys_clk_disable; >>> - >>> - /* >>> - * According to the hardware manual, a delay of at least 32 clock >>> - * cycles is required between de-asserting the clkgen reset and >>> - * de-asserting the GPU reset. Assuming a worst-case scenario with >>> - * a very high GPU clock frequency, a delay of 1 microsecond is >>> - * sufficient to ensure this requirement is met across all >>> - * feasible GPU clock speeds. >>> - */ >>> - udelay(1); >>> - >>> - err = reset_control_deassert(pvr_dev->reset); >>> - if (err) >>> - goto err_mem_clk_disable; >>> - >>> if (pvr_dev->fw_dev.booted) { >>> err = pvr_power_fw_enable(pvr_dev); >>> if (err) >>> - goto err_reset_assert; >>> + goto err_power_off; >>> } >>> >>> drm_dev_exit(idx); >>> >>> return 0; >>> >>> -err_reset_assert: >>> - reset_control_assert(pvr_dev->reset); >>> - >>> -err_mem_clk_disable: >>> - clk_disable_unprepare(pvr_dev->mem_clk); >>> - >>> -err_sys_clk_disable: >>> - clk_disable_unprepare(pvr_dev->sys_clk); >>> - >>> -err_core_clk_disable: >>> - clk_disable_unprepare(pvr_dev->core_clk); >>> +err_power_off: >>> + pvr_dev->device_data->pwr_ops->power_off(pvr_dev); >>> >>> err_drm_dev_exit: >>> drm_dev_exit(idx); >>> diff --git a/drivers/gpu/drm/imagination/pvr_power.h b/drivers/gpu/drm/imagination/pvr_power.h >>> index ada85674a7ca762dcf92df40424230e1c3910342..b853d092242cc90cb98cf66100679a309055a1dc 100644 >>> --- a/drivers/gpu/drm/imagination/pvr_power.h >>> +++ b/drivers/gpu/drm/imagination/pvr_power.h >>> @@ -41,4 +41,19 @@ pvr_power_put(struct pvr_device *pvr_dev) >>> int pvr_power_domains_init(struct pvr_device *pvr_dev); >>> void pvr_power_domains_fini(struct pvr_device *pvr_dev); >>> >>> +/** >>> + * struct pvr_power_sequence_ops - Platform specific power sequence operations. >>> + * @init: Pointer to the platform-specific initialization function. >>> + * @power_on: Pointer to the platform-specific power on function. >>> + * @power_off: Pointer to the platform-specific power off function. >>> + */ >>> +struct pvr_power_sequence_ops { >>> + int (*init)(struct pvr_device *pvr_dev); >>> + int (*power_on)(struct pvr_device *pvr_dev); >>> + int (*power_off)(struct pvr_device *pvr_dev); >>> +}; >>> + >>> +extern const struct pvr_power_sequence_ops pvr_power_sequence_ops_manual; >>> +extern const struct pvr_power_sequence_ops pvr_power_sequence_ops_pwrseq; >>> + >>> #endif /* PVR_POWER_H */ >>> >> >> > > Best regards, Best regards, -- Michal Wilczynski _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv