Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions Documentation/devicetree/bindings/usb/spacemit,k1-dwc3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/usb/spacemit,k1-dwc3.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: SpacemiT K1 SuperSpeed DWC3 USB SoC Controller

maintainers:
- Ze Huang <huang.ze@linux.dev>

description: |
The SpacemiT K1 embeds a DWC3 USB IP Core which supports Host functions
for USB 3.0 and DRD for USB 2.0.

Key features:
- USB3.0 SuperSpeed and USB2.0 High/Full/Low-Speed support
- Supports low-power modes (USB2.0 suspend, USB3.0 U1/U2/U3)
- Internal DMA controller and flexible endpoint FIFO sizing

Communication Interface:
- Use of PIPE3 (125MHz) interface for USB3.0 PHY
- Use of UTMI+ (30/60MHz) interface for USB2.0 PHY

allOf:
- $ref: snps,dwc3-common.yaml#

properties:
compatible:
const: spacemit,k1-dwc3

reg:
maxItems: 1

clocks:
maxItems: 1

clock-names:
const: usbdrd30

interrupts:
maxItems: 1

phys:
items:
- description: phandle to USB2/HS PHY
- description: phandle to USB3/SS PHY

phy-names:
items:
- const: usb2-phy
- const: usb3-phy

resets:
items:
- description: USB3.0 AHB reset
- description: USB3.0 VCC reset
- description: USB3.0 PHY reset
- description: PCIE0 global reset (for combo phy)

reset-names:
items:
- const: ahb
- const: vcc
- const: phy
- const: pcie0

reset-delay:
$ref: /schemas/types.yaml#/definitions/uint32
default: 2
description: delay after reset sequence [us]

vbus-supply:
description: A phandle to the regulator supplying the VBUS voltage.

required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- phys
- phy-names
- resets
- reset-names

unevaluatedProperties: false

examples:
- |
usb@c0a00000 {
compatible = "spacemit,k1-dwc3";
reg = <0xc0a00000 0x10000>;
clocks = <&syscon_apmu 16>;
clock-names = "usbdrd30";
interrupts = <125>;
phys = <&usb2phy>, <&usb3phy>;
phy-names = "usb2-phy", "usb3-phy";
resets = <&syscon_apmu 8>,
<&syscon_apmu 9>,
<&syscon_apmu 10>,
<&syscon_apmu 26>;
reset-names = "ahb", "vcc", "phy", "pcie0";
reset-delay = <2>;
vbus-supply = <&usb3_vbus>;
#address-cells = <1>;
#size-cells = <0>;

hub_2_0: hub@1 {
compatible = "usb2109,2817";
reg = <1>;
vdd-supply = <&usb3_vhub>;
peer-hub = <&hub_3_0>;
reset-gpios = <&gpio 3 28 1>;
};

hub_3_0: hub@2 {
compatible = "usb2109,817";
reg = <2>;
vdd-supply = <&usb3_vhub>;
peer-hub = <&hub_2_0>;
reset-gpios = <&gpio 3 28 1>;
};
};
11 changes: 11 additions & 0 deletions drivers/usb/dwc3/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,15 @@ config USB_DWC3_RTK
or dual-role mode.
Say 'Y' or 'M' if you have such device.

config USB_DWC3_GENERIC_PLAT
tristate "DWC3 Generic Platform Driver"
depends on OF && COMMON_CLK
default USB_DWC3
help
Support USB3 functionality in simple SoC integrations.
Currently supports SpacemiT DWC USB3. Platforms using
dwc3-of-simple can easily switch to dwc3-generic by flattening
the dwc3 child node in the device tree.
Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way.

endif
1 change: 1 addition & 0 deletions drivers/usb/dwc3/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o
167 changes: 167 additions & 0 deletions drivers/usb/dwc3/dwc3-generic-plat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* dwc3-generic-plat.c - DesignWare USB3 generic platform driver
*
* Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
*
* Inspired by dwc3-qcom.c and dwc3-of-simple.c
*/

#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include "glue.h"

struct dwc3_generic {
struct device *dev;
struct dwc3 dwc;
struct clk_bulk_data *clks;
int num_clocks;
struct reset_control *resets;
};

static void dwc3_generic_reset_control_assert(void *data)
{
reset_control_assert(data);
}

static int dwc3_generic_probe(struct platform_device *pdev)
{
struct dwc3_probe_data probe_data = {};
struct device *dev = &pdev->dev;
struct dwc3_generic *dwc3;
struct resource *res;
int ret;

dwc3 = devm_kzalloc(dev, sizeof(*dwc3), GFP_KERNEL);
if (!dwc3)
return -ENOMEM;

dwc3->dev = dev;

platform_set_drvdata(pdev, dwc3);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing memory resource\n");
return -ENODEV;
}

dwc3->resets = devm_reset_control_array_get_optional_exclusive(dev);
if (IS_ERR(dwc3->resets))
return dev_err_probe(dev, PTR_ERR(dwc3->resets), "failed to get resets\n");

ret = reset_control_assert(dwc3->resets);
if (ret)
return dev_err_probe(dev, ret, "failed to assert resets\n");

/* Not strict timing, just for safety */
udelay(2);

ret = reset_control_deassert(dwc3->resets);
if (ret)
return dev_err_probe(dev, ret, "failed to deassert resets\n");

ret = devm_add_action_or_reset(dev, dwc3_generic_reset_control_assert, dwc3->resets);
if (ret)
return ret;

ret = devm_clk_bulk_get_all_enabled(dwc3->dev, &dwc3->clks);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get clocks\n");

dwc3->num_clocks = ret;
dwc3->dwc.dev = dev;
probe_data.dwc = &dwc3->dwc;
probe_data.res = res;
probe_data.ignore_clocks_and_resets = true;
ret = dwc3_core_probe(&probe_data);
if (ret)
return dev_err_probe(dev, ret, "failed to register DWC3 Core\n");

return 0;
}

static void dwc3_generic_remove(struct platform_device *pdev)
{
struct dwc3_generic *dwc3 = platform_get_drvdata(pdev);

dwc3_core_remove(&dwc3->dwc);
}

static int dwc3_generic_suspend(struct device *dev)
{
struct dwc3_generic *dwc3 = dev_get_drvdata(dev);
int ret;

ret = dwc3_pm_suspend(&dwc3->dwc);
if (ret)
return ret;

clk_bulk_disable_unprepare(dwc3->num_clocks, dwc3->clks);

return 0;
}

static int dwc3_generic_resume(struct device *dev)
{
struct dwc3_generic *dwc3 = dev_get_drvdata(dev);
int ret;

ret = clk_bulk_prepare_enable(dwc3->num_clocks, dwc3->clks);
if (ret)
return ret;

ret = dwc3_pm_resume(&dwc3->dwc);
if (ret)
return ret;

return 0;
}

static int dwc3_generic_runtime_suspend(struct device *dev)
{
struct dwc3_generic *dwc3 = dev_get_drvdata(dev);

return dwc3_runtime_suspend(&dwc3->dwc);
}

static int dwc3_generic_runtime_resume(struct device *dev)
{
struct dwc3_generic *dwc3 = dev_get_drvdata(dev);

return dwc3_runtime_resume(&dwc3->dwc);
}

static int dwc3_generic_runtime_idle(struct device *dev)
{
struct dwc3_generic *dwc3 = dev_get_drvdata(dev);

return dwc3_runtime_idle(&dwc3->dwc);
}

static const struct dev_pm_ops dwc3_generic_dev_pm_ops = {
SYSTEM_SLEEP_PM_OPS(dwc3_generic_suspend, dwc3_generic_resume)
RUNTIME_PM_OPS(dwc3_generic_runtime_suspend, dwc3_generic_runtime_resume,
dwc3_generic_runtime_idle)
};

static const struct of_device_id dwc3_generic_of_match[] = {
{ .compatible = "spacemit,k1-dwc3", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dwc3_generic_of_match);

static struct platform_driver dwc3_generic_driver = {
.probe = dwc3_generic_probe,
.remove = dwc3_generic_remove,
.driver = {
.name = "dwc3-generic-plat",
.of_match_table = dwc3_generic_of_match,
.pm = pm_ptr(&dwc3_generic_dev_pm_ops),
},
};
module_platform_driver(dwc3_generic_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DesignWare USB3 generic platform driver");
Loading