From: Felipe Balbi <balbi@kernel.org>
To: Manish Narani <manish.narani@xilinx.com>,
gregkh@linuxfoundation.org, robh+dt@kernel.org,
michal.simek@xilinx.com, p.zabel@pengutronix.de
Cc: linux-usb@vger.kernel.org, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, git@xilinx.com,
Manish Narani <manish.narani@xilinx.com>
Subject: Re: [PATCH 2/2] usb: dwc3: Add driver for Xilinx platforms
Date: Thu, 27 Aug 2020 09:31:30 +0300 [thread overview]
Message-ID: <87y2m0ioql.fsf@kernel.org> (raw)
In-Reply-To: <1598467441-124203-3-git-send-email-manish.narani@xilinx.com>
[-- Attachment #1: Type: text/plain, Size: 11302 bytes --]
Manish Narani <manish.narani@xilinx.com> writes:
> Add a new driver for supporting Xilinx platforms. This driver handles
> the USB 3.0 PHY initialization and PIPE control & reset operations for
PHY initialization should be done as part of a drivers/phy driver.
> ZynqMP platforms. This also handles the USB 2.0 PHY initialization and
> reset operations for Versal platforms.
similarly for USB2 PHYs
> diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
> new file mode 100644
> index 000000000000..272906797a7a
> --- /dev/null
> +++ b/drivers/usb/dwc3/dwc3-xilinx.c
> @@ -0,0 +1,416 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver
> + *
> + * Authors: Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>
> + * Manish Narani <manish.narani@xilinx.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +#include <linux/of_address.h>
> +#include <linux/delay.h>
> +#include <linux/firmware/xlnx-zynqmp.h>
> +#include <linux/io.h>
> +
> +#include <linux/phy/phy.h>
> +
> +/* USB phy reset mask register */
> +#define XLNX_USB_PHY_RST 0x001C
> +#define XLNX_PHY_RST_MASK 0x1
> +
> +/* Xilinx USB 3.0 IP Register */
> +#define XLNX_USB_COHERENCY 0x005C
> +#define XLNX_USB_COHERENCY_ENABLE 0x1
> +
> +/* Versal USB Reset ID */
> +#define VERSAL_USB_RESET_ID 0xC104036
> +
> +#define PIPE_CLK_OFFSET 0x7c
> +#define PIPE_CLK_ON 1
> +#define PIPE_CLK_OFF 0
> +#define PIPE_POWER_OFFSET 0x80
> +#define PIPE_POWER_ON 1
> +#define PIPE_POWER_OFF 0
> +
> +#define RST_TIMEOUT 1000
> +
> +struct dwc3_xlnx {
> + int num_clocks;
> + struct clk_bulk_data *clks;
> + struct device *dev;
> + void __iomem *regs;
> + struct dwc3 *dwc;
> + struct phy *phy;
> + struct phy *usb3_phy;
> + struct reset_control *crst;
> + struct reset_control *hibrst;
> + struct reset_control *apbrst;
> +};
> +
> +static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
> +{
> + u32 reg;
> +
> + reg = readl(priv_data->regs + XLNX_USB_PHY_RST);
> +
> + if (mask)
> + /*
> + * Mask the phy reset signal from comtroller
s/comtroller/controller
But really, the comments don't bring any extra information. I'd say
remove the comments as the code speaks for itself very clearly in this
case.
> +static int dwc3_xlnx_rst_assert(struct reset_control *rstc)
this looks like it should be an actual reset controller driver.
> +static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
> +{
> + struct device *dev = priv_data->dev;
> + int ret;
> +
> + dwc3_xlnx_mask_phy_rst(priv_data, false);
> +
> + /* Assert and De-assert reset */
> + ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
> + PM_RESET_ACTION_ASSERT);
> + if (ret < 0) {
> + dev_err(dev, "failed to assert Reset\n");
> + return ret;
> + }
> +
> + ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
> + PM_RESET_ACTION_RELEASE);
> + if (ret < 0) {
> + dev_err(dev, "failed to De-assert Reset\n");
> + return ret;
> + }
> +
> + dwc3_xlnx_mask_phy_rst(priv_data, true);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
> +{
> + struct device *dev = priv_data->dev;
> + int ret;
> + u32 reg;
> +
> + priv_data->crst = devm_reset_control_get(dev, "usb_crst");
> + if (IS_ERR(priv_data->crst)) {
> + dev_err(dev, "failed to get %s reset signal\n", "usb_crst");
> + ret = PTR_ERR(priv_data->crst);
> + goto err;
> + }
> +
> + priv_data->hibrst = devm_reset_control_get(dev, "usb_hibrst");
> + if (IS_ERR(priv_data->hibrst)) {
> + dev_err(dev, "failed to get %s reset signal\n", "usb_hibrst");
> + ret = PTR_ERR(priv_data->hibrst);
> + goto err;
> + }
> +
> + priv_data->apbrst = devm_reset_control_get(dev, "usb_apbrst");
> + if (IS_ERR(priv_data->apbrst)) {
> + dev_err(dev, "failed to get %s reset signal\n", "usb_apbrst");
> + ret = PTR_ERR(priv_data->apbrst);
> + goto err;
> + }
> +
> + priv_data->usb3_phy = devm_phy_get(dev, "usb3-phy");
> +
> + ret = dwc3_xlnx_rst_assert(priv_data->crst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to assert reset\n",
> + __func__, __LINE__);
you don't need the function name or the line number here. Just improve
your error message a bit:
dev_err(dev, "Failed to assert usb3-phy reset\n");
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_assert(priv_data->hibrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to assert reset\n",
> + __func__, __LINE__);
dev_err(dev, "Failed to assert hibernation reset\n");
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_assert(priv_data->apbrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to assert reset\n",
> + __func__, __LINE__);
dev_err(dev, "Failed to assert APB reset\n");
> + goto err;
> + }
> +
> + ret = phy_init(priv_data->usb3_phy);
dwc3 core should be handling this already
> + if (ret < 0) {
> + phy_exit(priv_data->usb3_phy);
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_deassert(priv_data->apbrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to release reset\n",
> + __func__, __LINE__);
> + goto err;
> + }
> +
> + /* Set PIPE power present signal */
> + writel(PIPE_POWER_ON, priv_data->regs + PIPE_POWER_OFFSET);
> +
> + /* Clear PIPE CLK signal */
> + writel(PIPE_CLK_OFF, priv_data->regs + PIPE_CLK_OFFSET);
shouldn't this be hidden under clk_enable()?
> + ret = dwc3_xlnx_rst_deassert(priv_data->crst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to release reset\n",
> + __func__, __LINE__);
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_deassert(priv_data->hibrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to release reset\n",
> + __func__, __LINE__);
> + goto err;
> + }
> +
> + ret = phy_power_on(priv_data->usb3_phy);
> + if (ret < 0) {
> + phy_exit(priv_data->usb3_phy);
> + goto err;
> + }
> +
> + /*
> + * This routes the usb dma traffic to go through CCI path instead
> + * of reaching DDR directly. This traffic routing is needed to
> + * make SMMU and CCI work with USB dma.
> + */
> + if (of_dma_is_coherent(dev->of_node) || dev->iommu_group) {
> + reg = readl(priv_data->regs + XLNX_USB_COHERENCY);
> + reg |= XLNX_USB_COHERENCY_ENABLE;
> + writel(reg, priv_data->regs + XLNX_USB_COHERENCY);
> + }
> +
> +err:
> + return ret;
> +}
> +
> +static int dwc3_xlnx_probe(struct platform_device *pdev)
> +{
> + struct dwc3_xlnx *priv_data;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct resource *res;
> + void __iomem *regs;
> + int ret;
> +
> + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
> + if (!priv_data)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "missing memory resource\n");
> + return -ENODEV;
> + }
you don't need to check res here. devm_ioremap_resource() already does
that for you.
> + regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(regs))
> + return PTR_ERR(regs);
this looks like it could use an error message.
> + /* Store the usb control regs into priv_data for further usage */
pointless comment.
> + priv_data->regs = regs;
> +
unnecessary blank line.
> + priv_data->dev = dev;
> +
> + platform_set_drvdata(pdev, priv_data);
> +
> + ret = clk_bulk_get_all(priv_data->dev, &priv_data->clks);
> + if (ret < 0)
> + return ret;
> +
> + priv_data->num_clocks = ret;
> +
> + ret = clk_bulk_prepare_enable(priv_data->num_clocks, priv_data->clks);
> + if (ret)
> + return ret;
> +
> + if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-dwc3")) {
> + ret = dwc3_xlnx_init_zynqmp(priv_data);
> + if (ret)
> + goto err_clk_put;
> + }
instead, you could pass a pointer to dwc3_xlnx_init_zynqmp() as driver
data and just call it unconditionally. It would avoid the compatible
check here.
> + if (of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-dwc3")) {
> + ret = dwc3_xlnx_init_versal(priv_data);
> + if (ret)
> + goto err_clk_put;
> + }
same as above
> + ret = of_platform_populate(np, NULL, NULL, dev);
> + if (ret)
> + goto err_clk_put;
> +
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + pm_suspend_ignore_children(dev, false);
> + pm_runtime_get_sync(dev);
> +
> + pm_runtime_forbid(dev);
why forbid? You already have a get_sync()
> + return 0;
> +
> +err_clk_put:
> + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
> + clk_bulk_put_all(priv_data->num_clocks, priv_data->clks);
> +
> + return ret;
> +}
> +
> +static int dwc3_xlnx_remove(struct platform_device *pdev)
> +{
> + struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + of_platform_depopulate(dev);
> +
> + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
> + clk_bulk_put_all(priv_data->num_clocks, priv_data->clks);
> + priv_data->num_clocks = 0;
> +
> + pm_runtime_disable(dev);
> + pm_runtime_put_noidle(dev);
> + pm_runtime_set_suspended(dev);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int dwc3_xlnx_runtime_suspend(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_runtime_idle(struct device *dev)
> +{
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_autosuspend(dev);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_runtime_resume(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + return clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
> +}
> +#endif /* CONFIG_PM */
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int dwc3_xlnx_suspend(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + /* Disable the clocks */
> + clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_resume(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + return clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
> +}
you have the same implementation for both types of suspend/resume. Maybe
extract dwc3_xlnx_{suspend,resume}_common() and just call it from both
callbacks?
> +#endif /* CONFIG_PM_SLEEP */
> +
> +static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume)
> + SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend, dwc3_xlnx_runtime_resume,
> + dwc3_xlnx_runtime_idle)
seems like it could be replaced with UNIVERSAL_PM_OPS with minor
modifications.
--
balbi
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 857 bytes --]
WARNING: multiple messages have this Message-ID (diff)
From: Felipe Balbi <balbi@kernel.org>
To: Manish Narani <manish.narani@xilinx.com>,
gregkh@linuxfoundation.org, robh+dt@kernel.org,
michal.simek@xilinx.com, p.zabel@pengutronix.de
Cc: devicetree@vger.kernel.org, linux-usb@vger.kernel.org,
linux-kernel@vger.kernel.org,
Manish Narani <manish.narani@xilinx.com>,
git@xilinx.com, linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 2/2] usb: dwc3: Add driver for Xilinx platforms
Date: Thu, 27 Aug 2020 09:31:30 +0300 [thread overview]
Message-ID: <87y2m0ioql.fsf@kernel.org> (raw)
In-Reply-To: <1598467441-124203-3-git-send-email-manish.narani@xilinx.com>
[-- Attachment #1.1: Type: text/plain, Size: 11302 bytes --]
Manish Narani <manish.narani@xilinx.com> writes:
> Add a new driver for supporting Xilinx platforms. This driver handles
> the USB 3.0 PHY initialization and PIPE control & reset operations for
PHY initialization should be done as part of a drivers/phy driver.
> ZynqMP platforms. This also handles the USB 2.0 PHY initialization and
> reset operations for Versal platforms.
similarly for USB2 PHYs
> diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
> new file mode 100644
> index 000000000000..272906797a7a
> --- /dev/null
> +++ b/drivers/usb/dwc3/dwc3-xilinx.c
> @@ -0,0 +1,416 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * dwc3-xilinx.c - Xilinx DWC3 controller specific glue driver
> + *
> + * Authors: Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>
> + * Manish Narani <manish.narani@xilinx.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset.h>
> +#include <linux/of_address.h>
> +#include <linux/delay.h>
> +#include <linux/firmware/xlnx-zynqmp.h>
> +#include <linux/io.h>
> +
> +#include <linux/phy/phy.h>
> +
> +/* USB phy reset mask register */
> +#define XLNX_USB_PHY_RST 0x001C
> +#define XLNX_PHY_RST_MASK 0x1
> +
> +/* Xilinx USB 3.0 IP Register */
> +#define XLNX_USB_COHERENCY 0x005C
> +#define XLNX_USB_COHERENCY_ENABLE 0x1
> +
> +/* Versal USB Reset ID */
> +#define VERSAL_USB_RESET_ID 0xC104036
> +
> +#define PIPE_CLK_OFFSET 0x7c
> +#define PIPE_CLK_ON 1
> +#define PIPE_CLK_OFF 0
> +#define PIPE_POWER_OFFSET 0x80
> +#define PIPE_POWER_ON 1
> +#define PIPE_POWER_OFF 0
> +
> +#define RST_TIMEOUT 1000
> +
> +struct dwc3_xlnx {
> + int num_clocks;
> + struct clk_bulk_data *clks;
> + struct device *dev;
> + void __iomem *regs;
> + struct dwc3 *dwc;
> + struct phy *phy;
> + struct phy *usb3_phy;
> + struct reset_control *crst;
> + struct reset_control *hibrst;
> + struct reset_control *apbrst;
> +};
> +
> +static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
> +{
> + u32 reg;
> +
> + reg = readl(priv_data->regs + XLNX_USB_PHY_RST);
> +
> + if (mask)
> + /*
> + * Mask the phy reset signal from comtroller
s/comtroller/controller
But really, the comments don't bring any extra information. I'd say
remove the comments as the code speaks for itself very clearly in this
case.
> +static int dwc3_xlnx_rst_assert(struct reset_control *rstc)
this looks like it should be an actual reset controller driver.
> +static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
> +{
> + struct device *dev = priv_data->dev;
> + int ret;
> +
> + dwc3_xlnx_mask_phy_rst(priv_data, false);
> +
> + /* Assert and De-assert reset */
> + ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
> + PM_RESET_ACTION_ASSERT);
> + if (ret < 0) {
> + dev_err(dev, "failed to assert Reset\n");
> + return ret;
> + }
> +
> + ret = zynqmp_pm_reset_assert(VERSAL_USB_RESET_ID,
> + PM_RESET_ACTION_RELEASE);
> + if (ret < 0) {
> + dev_err(dev, "failed to De-assert Reset\n");
> + return ret;
> + }
> +
> + dwc3_xlnx_mask_phy_rst(priv_data, true);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
> +{
> + struct device *dev = priv_data->dev;
> + int ret;
> + u32 reg;
> +
> + priv_data->crst = devm_reset_control_get(dev, "usb_crst");
> + if (IS_ERR(priv_data->crst)) {
> + dev_err(dev, "failed to get %s reset signal\n", "usb_crst");
> + ret = PTR_ERR(priv_data->crst);
> + goto err;
> + }
> +
> + priv_data->hibrst = devm_reset_control_get(dev, "usb_hibrst");
> + if (IS_ERR(priv_data->hibrst)) {
> + dev_err(dev, "failed to get %s reset signal\n", "usb_hibrst");
> + ret = PTR_ERR(priv_data->hibrst);
> + goto err;
> + }
> +
> + priv_data->apbrst = devm_reset_control_get(dev, "usb_apbrst");
> + if (IS_ERR(priv_data->apbrst)) {
> + dev_err(dev, "failed to get %s reset signal\n", "usb_apbrst");
> + ret = PTR_ERR(priv_data->apbrst);
> + goto err;
> + }
> +
> + priv_data->usb3_phy = devm_phy_get(dev, "usb3-phy");
> +
> + ret = dwc3_xlnx_rst_assert(priv_data->crst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to assert reset\n",
> + __func__, __LINE__);
you don't need the function name or the line number here. Just improve
your error message a bit:
dev_err(dev, "Failed to assert usb3-phy reset\n");
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_assert(priv_data->hibrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to assert reset\n",
> + __func__, __LINE__);
dev_err(dev, "Failed to assert hibernation reset\n");
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_assert(priv_data->apbrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to assert reset\n",
> + __func__, __LINE__);
dev_err(dev, "Failed to assert APB reset\n");
> + goto err;
> + }
> +
> + ret = phy_init(priv_data->usb3_phy);
dwc3 core should be handling this already
> + if (ret < 0) {
> + phy_exit(priv_data->usb3_phy);
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_deassert(priv_data->apbrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to release reset\n",
> + __func__, __LINE__);
> + goto err;
> + }
> +
> + /* Set PIPE power present signal */
> + writel(PIPE_POWER_ON, priv_data->regs + PIPE_POWER_OFFSET);
> +
> + /* Clear PIPE CLK signal */
> + writel(PIPE_CLK_OFF, priv_data->regs + PIPE_CLK_OFFSET);
shouldn't this be hidden under clk_enable()?
> + ret = dwc3_xlnx_rst_deassert(priv_data->crst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to release reset\n",
> + __func__, __LINE__);
> + goto err;
> + }
> +
> + ret = dwc3_xlnx_rst_deassert(priv_data->hibrst);
> + if (ret < 0) {
> + dev_err(dev, "%s: %d: Failed to release reset\n",
> + __func__, __LINE__);
> + goto err;
> + }
> +
> + ret = phy_power_on(priv_data->usb3_phy);
> + if (ret < 0) {
> + phy_exit(priv_data->usb3_phy);
> + goto err;
> + }
> +
> + /*
> + * This routes the usb dma traffic to go through CCI path instead
> + * of reaching DDR directly. This traffic routing is needed to
> + * make SMMU and CCI work with USB dma.
> + */
> + if (of_dma_is_coherent(dev->of_node) || dev->iommu_group) {
> + reg = readl(priv_data->regs + XLNX_USB_COHERENCY);
> + reg |= XLNX_USB_COHERENCY_ENABLE;
> + writel(reg, priv_data->regs + XLNX_USB_COHERENCY);
> + }
> +
> +err:
> + return ret;
> +}
> +
> +static int dwc3_xlnx_probe(struct platform_device *pdev)
> +{
> + struct dwc3_xlnx *priv_data;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct resource *res;
> + void __iomem *regs;
> + int ret;
> +
> + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
> + if (!priv_data)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "missing memory resource\n");
> + return -ENODEV;
> + }
you don't need to check res here. devm_ioremap_resource() already does
that for you.
> + regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(regs))
> + return PTR_ERR(regs);
this looks like it could use an error message.
> + /* Store the usb control regs into priv_data for further usage */
pointless comment.
> + priv_data->regs = regs;
> +
unnecessary blank line.
> + priv_data->dev = dev;
> +
> + platform_set_drvdata(pdev, priv_data);
> +
> + ret = clk_bulk_get_all(priv_data->dev, &priv_data->clks);
> + if (ret < 0)
> + return ret;
> +
> + priv_data->num_clocks = ret;
> +
> + ret = clk_bulk_prepare_enable(priv_data->num_clocks, priv_data->clks);
> + if (ret)
> + return ret;
> +
> + if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-dwc3")) {
> + ret = dwc3_xlnx_init_zynqmp(priv_data);
> + if (ret)
> + goto err_clk_put;
> + }
instead, you could pass a pointer to dwc3_xlnx_init_zynqmp() as driver
data and just call it unconditionally. It would avoid the compatible
check here.
> + if (of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-dwc3")) {
> + ret = dwc3_xlnx_init_versal(priv_data);
> + if (ret)
> + goto err_clk_put;
> + }
same as above
> + ret = of_platform_populate(np, NULL, NULL, dev);
> + if (ret)
> + goto err_clk_put;
> +
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + pm_suspend_ignore_children(dev, false);
> + pm_runtime_get_sync(dev);
> +
> + pm_runtime_forbid(dev);
why forbid? You already have a get_sync()
> + return 0;
> +
> +err_clk_put:
> + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
> + clk_bulk_put_all(priv_data->num_clocks, priv_data->clks);
> +
> + return ret;
> +}
> +
> +static int dwc3_xlnx_remove(struct platform_device *pdev)
> +{
> + struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + of_platform_depopulate(dev);
> +
> + clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
> + clk_bulk_put_all(priv_data->num_clocks, priv_data->clks);
> + priv_data->num_clocks = 0;
> +
> + pm_runtime_disable(dev);
> + pm_runtime_put_noidle(dev);
> + pm_runtime_set_suspended(dev);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int dwc3_xlnx_runtime_suspend(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_runtime_idle(struct device *dev)
> +{
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_autosuspend(dev);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_runtime_resume(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + return clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
> +}
> +#endif /* CONFIG_PM */
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int dwc3_xlnx_suspend(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + /* Disable the clocks */
> + clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
> +
> + return 0;
> +}
> +
> +static int dwc3_xlnx_resume(struct device *dev)
> +{
> + struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
> +
> + return clk_bulk_enable(priv_data->num_clocks, priv_data->clks);
> +}
you have the same implementation for both types of suspend/resume. Maybe
extract dwc3_xlnx_{suspend,resume}_common() and just call it from both
callbacks?
> +#endif /* CONFIG_PM_SLEEP */
> +
> +static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(dwc3_xlnx_suspend, dwc3_xlnx_resume)
> + SET_RUNTIME_PM_OPS(dwc3_xlnx_runtime_suspend, dwc3_xlnx_runtime_resume,
> + dwc3_xlnx_runtime_idle)
seems like it could be replaced with UNIVERSAL_PM_OPS with minor
modifications.
--
balbi
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 857 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
next prev parent reply other threads:[~2020-08-27 6:31 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-08-26 18:43 [PATCH 0/2] Add a separate DWC3 OF driver for Xilinx platforms Manish Narani
2020-08-26 18:43 ` Manish Narani
2020-08-26 18:44 ` [PATCH 1/2] dt-bindings: usb: dwc3-xilinx: Add documentation for Versal DWC3 Controller Manish Narani
2020-08-26 18:44 ` Manish Narani
2020-09-08 23:05 ` Rob Herring
2020-09-08 23:05 ` Rob Herring
2020-09-09 15:46 ` Manish Narani
2020-09-09 15:46 ` Manish Narani
2020-09-09 16:00 ` Rob Herring
2020-09-09 16:00 ` Rob Herring
2020-08-26 18:44 ` [PATCH 2/2] usb: dwc3: Add driver for Xilinx platforms Manish Narani
2020-08-26 18:44 ` Manish Narani
2020-08-26 19:00 ` Randy Dunlap
2020-08-26 19:00 ` Randy Dunlap
2020-08-27 6:31 ` Felipe Balbi [this message]
2020-08-27 6:31 ` Felipe Balbi
2020-08-28 11:41 ` Manish Narani
2020-08-28 11:41 ` Manish Narani
2020-09-01 12:15 ` Felipe Balbi
2020-09-01 12:15 ` Felipe Balbi
2020-09-09 9:14 ` Manish Narani
2020-09-09 9:14 ` Manish Narani
2020-09-09 10:26 ` Felipe Balbi
2020-09-09 10:26 ` Felipe Balbi
2020-08-27 7:42 ` Chunfeng Yun
2020-08-27 7:42 ` Chunfeng Yun
2020-08-27 9:02 ` Philipp Zabel
2020-08-27 9:02 ` Philipp Zabel
2020-08-27 18:46 ` Robin Murphy
2020-08-27 18:46 ` Robin Murphy
2020-08-28 17:53 ` Manish Narani
2020-08-28 17:53 ` Manish Narani
2020-09-01 12:00 ` Robin Murphy
2020-09-01 12:00 ` Robin Murphy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87y2m0ioql.fsf@kernel.org \
--to=balbi@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=git@xilinx.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=manish.narani@xilinx.com \
--cc=michal.simek@xilinx.com \
--cc=p.zabel@pengutronix.de \
--cc=robh+dt@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.