aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r--drivers/usb/chipidea/Kconfig4
-rw-r--r--drivers/usb/chipidea/Makefile1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_npcm.c114
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c16
-rw-r--r--drivers/usb/chipidea/ci_hdrc_usb2.c13
-rw-r--r--drivers/usb/chipidea/host.c48
-rw-r--r--drivers/usb/chipidea/otg.c5
7 files changed, 156 insertions, 45 deletions
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index c815824a0b2d..bab45bc62361 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -43,6 +43,10 @@ config USB_CHIPIDEA_MSM
tristate "Enable MSM hsusb glue driver" if EXPERT
default USB_CHIPIDEA
+config USB_CHIPIDEA_NPCM
+ tristate "Enable NPCM hsusb glue driver" if EXPERT
+ default USB_CHIPIDEA
+
config USB_CHIPIDEA_IMX
tristate "Enable i.MX USB glue driver" if EXPERT
depends on OF
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 71afeab97e83..718cb24603dd 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -13,6 +13,7 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
obj-$(CONFIG_USB_CHIPIDEA_GENERIC) += ci_hdrc_usb2.o
obj-$(CONFIG_USB_CHIPIDEA_MSM) += ci_hdrc_msm.o
+obj-$(CONFIG_USB_CHIPIDEA_NPCM) += ci_hdrc_npcm.o
obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
obj-$(CONFIG_USB_CHIPIDEA_IMX) += usbmisc_imx.o ci_hdrc_imx.o
obj-$(CONFIG_USB_CHIPIDEA_TEGRA) += ci_hdrc_tegra.o
diff --git a/drivers/usb/chipidea/ci_hdrc_npcm.c b/drivers/usb/chipidea/ci_hdrc_npcm.c
new file mode 100644
index 000000000000..e4a191e02ceb
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_npcm.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2023 Nuvoton Technology corporation.
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/reset-controller.h>
+#include <linux/of.h>
+
+#include "ci.h"
+
+struct npcm_udc_data {
+ struct platform_device *ci;
+ struct clk *core_clk;
+ struct ci_hdrc_platform_data pdata;
+};
+
+static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
+{
+ struct device *dev = ci->dev->parent;
+
+ switch (event) {
+ case CI_HDRC_CONTROLLER_RESET_EVENT:
+ /* clear all mode bits */
+ hw_write(ci, OP_USBMODE, 0xffffffff, 0x0);
+ break;
+ default:
+ dev_dbg(dev, "unknown ci_hdrc event (%d)\n",event);
+ break;
+ }
+
+ return 0;
+}
+
+static int npcm_udc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct npcm_udc_data *ci;
+ struct platform_device *plat_ci;
+ struct device *dev = &pdev->dev;
+
+ ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+ if (!ci)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ci);
+
+ ci->core_clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(ci->core_clk))
+ return PTR_ERR(ci->core_clk);
+
+ ret = clk_prepare_enable(ci->core_clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable the clock: %d\n", ret);
+
+ ci->pdata.name = dev_name(dev);
+ ci->pdata.capoffset = DEF_CAPOFFSET;
+ ci->pdata.flags = CI_HDRC_REQUIRES_ALIGNED_DMA |
+ CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS;
+ ci->pdata.phy_mode = USBPHY_INTERFACE_MODE_UTMI;
+ ci->pdata.notify_event = npcm_udc_notify_event;
+
+ plat_ci = ci_hdrc_add_device(dev, pdev->resource, pdev->num_resources,
+ &ci->pdata);
+ if (IS_ERR(plat_ci)) {
+ ret = PTR_ERR(plat_ci);
+ dev_err(dev, "failed to register HDRC NPCM device: %d\n", ret);
+ goto clk_err;
+ }
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+clk_err:
+ clk_disable_unprepare(ci->core_clk);
+ return ret;
+}
+
+static int npcm_udc_remove(struct platform_device *pdev)
+{
+ struct npcm_udc_data *ci = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ ci_hdrc_remove_device(ci->ci);
+ clk_disable_unprepare(ci->core_clk);
+
+ return 0;
+}
+
+static const struct of_device_id npcm_udc_dt_match[] = {
+ { .compatible = "nuvoton,npcm750-udc", },
+ { .compatible = "nuvoton,npcm845-udc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, npcm_udc_dt_match);
+
+static struct platform_driver npcm_udc_driver = {
+ .probe = npcm_udc_probe,
+ .remove = npcm_udc_remove,
+ .driver = {
+ .name = "npcm_udc",
+ .of_match_table = npcm_udc_dt_match,
+ },
+};
+
+module_platform_driver(npcm_udc_driver);
+
+MODULE_DESCRIPTION("NPCM USB device controller driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 8e78bf643e25..2cc305803217 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -293,14 +293,12 @@ static int tegra_usb_probe(struct platform_device *pdev)
usb->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(usb->phy))
return dev_err_probe(&pdev->dev, PTR_ERR(usb->phy),
- "failed to get PHY\n");
+ "failed to get PHY");
usb->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(usb->clk)) {
- err = PTR_ERR(usb->clk);
- dev_err(&pdev->dev, "failed to get clock: %d\n", err);
- return err;
- }
+ if (IS_ERR(usb->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(usb->clk),
+ "failed to get clock");
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
@@ -316,7 +314,7 @@ static int tegra_usb_probe(struct platform_device *pdev)
err = tegra_usb_reset_controller(&pdev->dev);
if (err) {
- dev_err(&pdev->dev, "failed to reset controller: %d\n", err);
+ dev_err_probe(&pdev->dev, err, "failed to reset controller");
goto fail_power_off;
}
@@ -347,8 +345,8 @@ static int tegra_usb_probe(struct platform_device *pdev)
usb->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
pdev->num_resources, &usb->data);
if (IS_ERR(usb->dev)) {
- err = PTR_ERR(usb->dev);
- dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err);
+ err = dev_err_probe(&pdev->dev, PTR_ERR(usb->dev),
+ "failed to add HDRC device");
goto phy_shutdown;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
index 1321ee67f3b8..97379f653b06 100644
--- a/drivers/usb/chipidea/ci_hdrc_usb2.c
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -9,9 +9,9 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/usb/chipidea.h>
#include <linux/usb/hcd.h>
#include <linux/usb/ulpi.h>
@@ -51,8 +51,8 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct ci_hdrc_usb2_priv *priv;
struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
+ const struct ci_hdrc_platform_data *data;
int ret;
- const struct of_device_id *match;
if (!ci_pdata) {
ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL);
@@ -61,11 +61,10 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
*ci_pdata = ci_default_pdata; /* struct copy */
}
- match = of_match_device(ci_hdrc_usb2_of_match, &pdev->dev);
- if (match && match->data) {
+ data = device_get_match_data(&pdev->dev);
+ if (data)
/* struct copy */
- *ci_pdata = *(struct ci_hdrc_platform_data *)match->data;
- }
+ *ci_pdata = *data;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -120,7 +119,7 @@ static struct platform_driver ci_hdrc_usb2_driver = {
.remove_new = ci_hdrc_usb2_remove,
.driver = {
.name = "chipidea-usb2",
- .of_match_table = of_match_ptr(ci_hdrc_usb2_of_match),
+ .of_match_table = ci_hdrc_usb2_of_match,
},
};
module_platform_driver(ci_hdrc_usb2_driver);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 08af26b762a2..0cce19208370 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -30,8 +30,7 @@ struct ehci_ci_priv {
};
struct ci_hdrc_dma_aligned_buffer {
- void *kmalloc_ptr;
- void *old_xfer_buffer;
+ void *original_buffer;
u8 data[];
};
@@ -380,59 +379,52 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
return 0;
}
-static void ci_hdrc_free_dma_aligned_buffer(struct urb *urb)
+static void ci_hdrc_free_dma_aligned_buffer(struct urb *urb, bool copy_back)
{
struct ci_hdrc_dma_aligned_buffer *temp;
- size_t length;
if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
return;
+ urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
temp = container_of(urb->transfer_buffer,
struct ci_hdrc_dma_aligned_buffer, data);
+ urb->transfer_buffer = temp->original_buffer;
+
+ if (copy_back && usb_urb_dir_in(urb)) {
+ size_t length;
- if (usb_urb_dir_in(urb)) {
if (usb_pipeisoc(urb->pipe))
length = urb->transfer_buffer_length;
else
length = urb->actual_length;
- memcpy(temp->old_xfer_buffer, temp->data, length);
+ memcpy(temp->original_buffer, temp->data, length);
}
- urb->transfer_buffer = temp->old_xfer_buffer;
- kfree(temp->kmalloc_ptr);
- urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+ kfree(temp);
}
static int ci_hdrc_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
{
- struct ci_hdrc_dma_aligned_buffer *temp, *kmalloc_ptr;
- const unsigned int ci_hdrc_usb_dma_align = 32;
- size_t kmalloc_size;
+ struct ci_hdrc_dma_aligned_buffer *temp;
- if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0 ||
- !((uintptr_t)urb->transfer_buffer & (ci_hdrc_usb_dma_align - 1)))
+ if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
+ return 0;
+ if (IS_ALIGNED((uintptr_t)urb->transfer_buffer, 4)
+ && IS_ALIGNED(urb->transfer_buffer_length, 4))
return 0;
- /* Allocate a buffer with enough padding for alignment */
- kmalloc_size = urb->transfer_buffer_length +
- sizeof(struct ci_hdrc_dma_aligned_buffer) +
- ci_hdrc_usb_dma_align - 1;
-
- kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
- if (!kmalloc_ptr)
+ temp = kmalloc(sizeof(*temp) + ALIGN(urb->transfer_buffer_length, 4), mem_flags);
+ if (!temp)
return -ENOMEM;
- /* Position our struct dma_aligned_buffer such that data is aligned */
- temp = PTR_ALIGN(kmalloc_ptr + 1, ci_hdrc_usb_dma_align) - 1;
- temp->kmalloc_ptr = kmalloc_ptr;
- temp->old_xfer_buffer = urb->transfer_buffer;
if (usb_urb_dir_out(urb))
memcpy(temp->data, urb->transfer_buffer,
urb->transfer_buffer_length);
- urb->transfer_buffer = temp->data;
+ temp->original_buffer = urb->transfer_buffer;
+ urb->transfer_buffer = temp->data;
urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
return 0;
@@ -449,7 +441,7 @@ static int ci_hdrc_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
if (ret)
- ci_hdrc_free_dma_aligned_buffer(urb);
+ ci_hdrc_free_dma_aligned_buffer(urb, false);
return ret;
}
@@ -457,7 +449,7 @@ static int ci_hdrc_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
static void ci_hdrc_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
usb_hcd_unmap_urb_for_dma(hcd, urb);
- ci_hdrc_free_dma_aligned_buffer(urb);
+ ci_hdrc_free_dma_aligned_buffer(urb, true);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index f5490f2a5b6b..647e98f4e351 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -130,8 +130,11 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
- if (!ci->is_otg)
+ if (!ci->is_otg) {
+ if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS)
+ usb_gadget_vbus_connect(&ci->gadget);
return;
+ }
if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget);