usb: dwc3: dwc3-generic: check the parent nodes
The kernel devicetree has definitions for port and hub nodes as subnodes
to the USB devices. These subnodes don't contain all of the data required
to properly configure the dwc3. Check the parent nodes if the data is not
in the port/hub node.
Here's an example from the librem5 kernel dts file
&usb_dwc3_0 {
#address-cells = <1>;
#size-cells = <0>;
dr_mode = "otg";
snps,dis_u3_susphy_quirk;
status = "okay";
port@0 {
reg = <0>;
typec_hs: endpoint {
remote-endpoint = <&usb_con_hs>;
};
};
port@1 {
reg = <1>;
typec_ss: endpoint {
remote-endpoint = <&usb_con_ss>;
};
};
};
&usb_dwc3_1 {
dr_mode = "host";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
/* Microchip USB2642 */
hub@1 {
compatible = "usb424,2640";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
mass-storage@1 {
compatible = "usb424,4041";
reg = <1>;
};
};
};
Signed-off-by: Angus Ainslie <angus@akkea.ca>
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 8d53ba7..01bd0ca 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -110,7 +110,12 @@
struct dwc3_generic_plat *plat = dev_get_plat(dev);
ofnode node = dev_ofnode(dev);
- plat->base = dev_read_addr(dev);
+ if (!strncmp(dev->name, "port", 4) || !strncmp(dev->name, "hub", 3)) {
+ /* This is a leaf so check the parent */
+ plat->base = dev_read_addr(dev->parent);
+ } else {
+ plat->base = dev_read_addr(dev);
+ }
plat->maximum_speed = usb_get_maximum_speed(node);
if (plat->maximum_speed == USB_SPEED_UNKNOWN) {
@@ -120,8 +125,13 @@
plat->dr_mode = usb_get_dr_mode(node);
if (plat->dr_mode == USB_DR_MODE_UNKNOWN) {
- pr_err("Invalid usb mode setup\n");
- return -ENODEV;
+ /* might be a leaf so check the parent for mode */
+ node = dev_ofnode(dev->parent);
+ plat->dr_mode = usb_get_dr_mode(node);
+ if (plat->dr_mode == USB_DR_MODE_UNKNOWN) {
+ pr_err("Invalid usb mode setup\n");
+ return -ENODEV;
+ }
}
return 0;
@@ -301,16 +311,20 @@
{
ofnode node;
int ret;
+ enum usb_dr_mode dr_mode;
+
+ dr_mode = usb_get_dr_mode(dev_ofnode(parent));
ofnode_for_each_subnode(node, dev_ofnode(parent)) {
const char *name = ofnode_get_name(node);
- enum usb_dr_mode dr_mode;
struct udevice *dev;
const char *driver = NULL;
debug("%s: subnode name: %s\n", __func__, name);
- dr_mode = usb_get_dr_mode(node);
+ /* if the parent node doesn't have a mode check the leaf */
+ if (!dr_mode)
+ dr_mode = usb_get_dr_mode(node);
switch (dr_mode) {
case USB_DR_MODE_PERIPHERAL:
@@ -450,6 +464,7 @@
{ .compatible = "rockchip,rk3328-dwc3" },
{ .compatible = "rockchip,rk3399-dwc3" },
{ .compatible = "qcom,dwc3" },
+ { .compatible = "fsl,imx8mq-dwc3" },
{ .compatible = "intel,tangier-dwc3" },
{ }
};