diff options
Diffstat (limited to 'drivers/usb/cdns3')
| -rw-r--r-- | drivers/usb/cdns3/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/usb/cdns3/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-gadget.c | 15 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-imx.c | 30 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-plat.c | 5 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-starfive.c | 246 | ||||
| -rw-r--r-- | drivers/usb/cdns3/cdns3-ti.c | 6 | 
7 files changed, 289 insertions, 25 deletions
| diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index b98ca0a1352a..0a514b591527 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -78,6 +78,17 @@ config USB_CDNS3_IMX  	  For example, imx8qm and imx8qxp. +config USB_CDNS3_STARFIVE +	tristate "Cadence USB3 support on StarFive SoC platforms" +	depends on ARCH_STARFIVE || COMPILE_TEST +	help +	  Say 'Y' or 'M' here if you are building for StarFive SoCs +	  platforms that contain Cadence USB3 controller core. + +	  e.g. JH7110. + +	  If you choose to build this driver as module it will +	  be dynamically linked and module will be called cdns3-starfive.ko  endif  if USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 61edb2f89276..48dfae75b5aa 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -24,6 +24,7 @@ endif  obj-$(CONFIG_USB_CDNS3_PCI_WRAP)		+= cdns3-pci-wrap.o  obj-$(CONFIG_USB_CDNS3_TI)			+= cdns3-ti.o  obj-$(CONFIG_USB_CDNS3_IMX)			+= cdns3-imx.o +obj-$(CONFIG_USB_CDNS3_STARFIVE)		+= cdns3-starfive.o  cdnsp-udc-pci-y					:= cdnsp-pci.o diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 1dcadef933e3..ea19253fd2d0 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -800,7 +800,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,  	if (request->status == -EINPROGRESS)  		request->status = status; -	usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request, +	if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) +		usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,  					priv_ep->dir);  	if ((priv_req->flags & REQUEST_UNALIGNED) && @@ -808,10 +809,10 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,  		/* Make DMA buffer CPU accessible */  		dma_sync_single_for_cpu(priv_dev->sysdev,  			priv_req->aligned_buf->dma, -			priv_req->aligned_buf->size, +			request->actual,  			priv_req->aligned_buf->dir);  		memcpy(request->buf, priv_req->aligned_buf->buf, -		       request->length); +		       request->actual);  	}  	priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); @@ -2543,10 +2544,12 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,  	if (ret < 0)  		return ret; -	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, +	if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) { +		ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,  					    usb_endpoint_dir_in(ep->desc)); -	if (ret) -		return ret; +		if (ret) +			return ret; +	}  	list_add_tail(&request->list, &priv_ep->deferred_req_list); diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 59860d1753fd..281de47e2a3b 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -105,11 +105,11 @@ static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value)  }  static const struct clk_bulk_data imx_cdns3_core_clks[] = { -	{ .id = "usb3_lpm_clk" }, -	{ .id = "usb3_bus_clk" }, -	{ .id = "usb3_aclk" }, -	{ .id = "usb3_ipg_clk" }, -	{ .id = "usb3_core_pclk" }, +	{ .id = "lpm" }, +	{ .id = "bus" }, +	{ .id = "aclk" }, +	{ .id = "ipg" }, +	{ .id = "core" },  };  static int cdns_imx_noncore_init(struct cdns_imx *data) @@ -218,7 +218,7 @@ err:  	return ret;  } -static int cdns_imx_remove(struct platform_device *pdev) +static void cdns_imx_remove(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev;  	struct cdns_imx *data = dev_get_drvdata(dev); @@ -229,8 +229,6 @@ static int cdns_imx_remove(struct platform_device *pdev)  	pm_runtime_disable(dev);  	pm_runtime_put_noidle(dev);  	platform_set_drvdata(pdev, NULL); - -	return 0;  }  #ifdef CONFIG_PM @@ -375,14 +373,22 @@ static inline bool cdns_imx_is_power_lost(struct cdns_imx *data)  		return false;  } +static int __maybe_unused cdns_imx_system_suspend(struct device *dev) +{ +	pm_runtime_put_sync(dev); +	return 0; +} +  static int __maybe_unused cdns_imx_system_resume(struct device *dev)  {  	struct cdns_imx *data = dev_get_drvdata(dev);  	int ret; -	ret = cdns_imx_resume(dev); -	if (ret) +	ret = pm_runtime_resume_and_get(dev); +	if (ret < 0) { +		dev_err(dev, "Could not get runtime PM.\n");  		return ret; +	}  	if (cdns_imx_is_power_lost(data)) {  		dev_dbg(dev, "resume from power lost\n"); @@ -405,7 +411,7 @@ static int cdns_imx_platform_suspend(struct device *dev,  static const struct dev_pm_ops cdns_imx_pm_ops = {  	SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) -	SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_suspend, cdns_imx_system_resume) +	SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_system_suspend, cdns_imx_system_resume)  };  static const struct of_device_id cdns_imx_of_match[] = { @@ -416,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match);  static struct platform_driver cdns_imx_driver = {  	.probe		= cdns_imx_probe, -	.remove		= cdns_imx_remove, +	.remove_new	= cdns_imx_remove,  	.driver		= {  		.name	= "cdns3-imx",  		.of_match_table	= cdns_imx_of_match, diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 2bc5d094548b..884e2301237f 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -175,7 +175,7 @@ err_phy3_init:   *   * Returns 0 on success otherwise negative errno   */ -static int cdns3_plat_remove(struct platform_device *pdev) +static void cdns3_plat_remove(struct platform_device *pdev)  {  	struct cdns *cdns = platform_get_drvdata(pdev);  	struct device *dev = cdns->dev; @@ -187,7 +187,6 @@ static int cdns3_plat_remove(struct platform_device *pdev)  	set_phy_power_off(cdns);  	phy_exit(cdns->usb2_phy);  	phy_exit(cdns->usb3_phy); -	return 0;  }  #ifdef CONFIG_PM @@ -320,7 +319,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match);  static struct platform_driver cdns3_driver = {  	.probe		= cdns3_plat_probe, -	.remove		= cdns3_plat_remove, +	.remove_new	= cdns3_plat_remove,  	.driver		= {  		.name	= "cdns-usb3",  		.of_match_table	= of_match_ptr(of_cdns3_match), diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 000000000000..fc1f003b145d --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author:	Minda Chen <minda.chen@starfivetech.com> + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/reset.h> +#include <linux/regmap.h> +#include <linux/usb/otg.h> +#include "core.h" + +#define USB_STRAP_HOST			BIT(17) +#define USB_STRAP_DEVICE		BIT(18) +#define USB_STRAP_MASK			GENMASK(18, 16) + +#define USB_SUSPENDM_HOST		BIT(19) +#define USB_SUSPENDM_MASK		BIT(19) + +#define USB_MISC_CFG_MASK		GENMASK(23, 20) +#define USB_SUSPENDM_BYPS		BIT(20) +#define USB_PLL_EN			BIT(22) +#define USB_REFCLK_MODE			BIT(23) + +struct cdns_starfive { +	struct device *dev; +	struct regmap *stg_syscon; +	struct reset_control *resets; +	struct clk_bulk_data *clks; +	int num_clks; +	u32 stg_usb_mode; +}; + +static void cdns_mode_init(struct platform_device *pdev, +			   struct cdns_starfive *data) +{ +	enum usb_dr_mode mode; + +	regmap_update_bits(data->stg_syscon, data->stg_usb_mode, +			   USB_MISC_CFG_MASK, +			   USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); + +	/* dr mode setting */ +	mode = usb_get_dr_mode(&pdev->dev); + +	switch (mode) { +	case USB_DR_MODE_HOST: +		regmap_update_bits(data->stg_syscon, +				   data->stg_usb_mode, +				   USB_STRAP_MASK, +				   USB_STRAP_HOST); +		regmap_update_bits(data->stg_syscon, +				   data->stg_usb_mode, +				   USB_SUSPENDM_MASK, +				   USB_SUSPENDM_HOST); +		break; + +	case USB_DR_MODE_PERIPHERAL: +		regmap_update_bits(data->stg_syscon, data->stg_usb_mode, +				   USB_STRAP_MASK, USB_STRAP_DEVICE); +		regmap_update_bits(data->stg_syscon, data->stg_usb_mode, +				   USB_SUSPENDM_MASK, 0); +		break; +	default: +		break; +	} +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ +	int ret; + +	ret = clk_bulk_prepare_enable(data->num_clks, data->clks); +	if (ret) +		return dev_err_probe(data->dev, ret, +				     "failed to enable clocks\n"); + +	ret = reset_control_deassert(data->resets); +	if (ret) { +		dev_err(data->dev, "failed to reset clocks\n"); +		goto err_clk_init; +	} + +	return ret; + +err_clk_init: +	clk_bulk_disable_unprepare(data->num_clks, data->clks); +	return ret; +} + +static void cdns_clk_rst_deinit(struct cdns_starfive *data) +{ +	reset_control_assert(data->resets); +	clk_bulk_disable_unprepare(data->num_clks, data->clks); +} + +static int cdns_starfive_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct cdns_starfive *data; +	unsigned int args; +	int ret; + +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	data->dev = dev; + +	data->stg_syscon = +		syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, +						     "starfive,stg-syscon", 1, &args); + +	if (IS_ERR(data->stg_syscon)) +		return dev_err_probe(dev, PTR_ERR(data->stg_syscon), +				     "Failed to parse starfive,stg-syscon\n"); + +	data->stg_usb_mode = args; + +	data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks); +	if (data->num_clks < 0) +		return dev_err_probe(data->dev, -ENODEV, +				     "Failed to get clocks\n"); + +	data->resets = devm_reset_control_array_get_exclusive(data->dev); +	if (IS_ERR(data->resets)) +		return dev_err_probe(data->dev, PTR_ERR(data->resets), +				     "Failed to get resets"); + +	cdns_mode_init(pdev, data); +	ret = cdns_clk_rst_init(data); +	if (ret) +		return ret; + +	ret = of_platform_populate(dev->of_node, NULL, NULL, dev); +	if (ret) { +		dev_err(dev, "Failed to create children\n"); +		cdns_clk_rst_deinit(data); +		return ret; +	} + +	device_set_wakeup_capable(dev, true); +	pm_runtime_set_active(dev); +	pm_runtime_enable(dev); +	platform_set_drvdata(pdev, data); + +	return 0; +} + +static int cdns_starfive_remove_core(struct device *dev, void *c) +{ +	struct platform_device *pdev = to_platform_device(dev); + +	platform_device_unregister(pdev); + +	return 0; +} + +static int cdns_starfive_remove(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct cdns_starfive *data = dev_get_drvdata(dev); + +	pm_runtime_get_sync(dev); +	device_for_each_child(dev, NULL, cdns_starfive_remove_core); + +	pm_runtime_disable(dev); +	pm_runtime_put_noidle(dev); +	cdns_clk_rst_deinit(data); +	platform_set_drvdata(pdev, NULL); + +	return 0; +} + +#ifdef CONFIG_PM +static int cdns_starfive_runtime_resume(struct device *dev) +{ +	struct cdns_starfive *data = dev_get_drvdata(dev); + +	return clk_bulk_prepare_enable(data->num_clks, data->clks); +} + +static int cdns_starfive_runtime_suspend(struct device *dev) +{ +	struct cdns_starfive *data = dev_get_drvdata(dev); + +	clk_bulk_disable_unprepare(data->num_clks, data->clks); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cdns_starfive_resume(struct device *dev) +{ +	struct cdns_starfive *data = dev_get_drvdata(dev); + +	return cdns_clk_rst_init(data); +} + +static int cdns_starfive_suspend(struct device *dev) +{ +	struct cdns_starfive *data = dev_get_drvdata(dev); + +	cdns_clk_rst_deinit(data); + +	return 0; +} +#endif +#endif + +static const struct dev_pm_ops cdns_starfive_pm_ops = { +	SET_RUNTIME_PM_OPS(cdns_starfive_runtime_suspend, +			   cdns_starfive_runtime_resume, NULL) +	SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume) +}; + +static const struct of_device_id cdns_starfive_of_match[] = { +	{ .compatible = "starfive,jh7110-usb", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); + +static struct platform_driver cdns_starfive_driver = { +	.probe		= cdns_starfive_probe, +	.remove		= cdns_starfive_remove, +	.driver		= { +		.name	= "cdns3-starfive", +		.of_match_table	= cdns_starfive_of_match, +		.pm	= &cdns_starfive_pm_ops, +	}, +}; +module_platform_driver(cdns_starfive_driver); + +MODULE_ALIAS("platform:cdns3-starfive"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer"); diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 07c318770362..81b9132e3aaa 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -199,7 +199,7 @@ static int cdns_ti_remove_core(struct device *dev, void *c)  	return 0;  } -static int cdns_ti_remove(struct platform_device *pdev) +static void cdns_ti_remove(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -208,8 +208,6 @@ static int cdns_ti_remove(struct platform_device *pdev)  	pm_runtime_disable(dev);  	platform_set_drvdata(pdev, NULL); - -	return 0;  }  static const struct of_device_id cdns_ti_of_match[] = { @@ -221,7 +219,7 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match);  static struct platform_driver cdns_ti_driver = {  	.probe		= cdns_ti_probe, -	.remove		= cdns_ti_remove, +	.remove_new	= cdns_ti_remove,  	.driver		= {  		.name	= "cdns3-ti",  		.of_match_table	= cdns_ti_of_match, | 
