[PATCH v8 00/14] Add support for MediaTek xHCI host controller

The series of patches are used to support xHCI host controller on MediaTek SoCs which has a glue layer IPPC (IP Port Control), and add USB function on T-PHY including T-PHY V1 and V2; Finally add USB related nodes for MT7629 platform.
The files of xhci-mtk.* and phy-mtk-tphy.* are ported from Linux Kenrel 5.6-rc1
v8 changes: 1. fix build-error reported by Marek in Part 5 2. make 2 functions static in part 10 suggested by Jagan 3. added review by Jagan on parts 4 + 10
v7 changes: 1. add @index for nodes with reg property suggested by Fabio 2. provide phy bulk init/exit/power on/off suggested by Jagan
v6 changes: 1. add Reviewed-by Simon & Weijie
v5 changes: 1. print error number suggested by Marek 2. support interrupt transfer 3. update MAINTAINER about MediaTek
v4 changes: 1. add phy_bulk API 2. use phy_bulk in xhci-mtk driver, also include dwc3/dwc2 drivers
v3 changes: 1. provide non/inline edev_get_child_count() suggested by Simon 2. squash [PATCH v2 02/10] into [PATCH v2 03/10] suggested by Simon 3. use macros to access IPPC registers suggested by Marek
v2 changes: 1. move ofnode_get_child_count() into ofnode.c suggested by Simon 2. add a test item for ofnode_get_child_count() suggested by Simon 3. use clk_bulk to get clocks suggested by Marek 4. use clrsetbits_le32() etc suggeseted by Marek 5. get the count of phys by dev_get_child_count() 6. drop reference to fixed clock clk20m
Chunfeng Yun (14): dm: core: Add function to get child count of ofnode or device test: dm: add test item for ofnode_get_child_count() phy: Add API for a bulk of phys test: dm: phy: add a test item for the phy_bulk API usb: dwc3: use the phy bulk API to get phys usb: dwc2_udc_otg: use the phy bulk API to get phys phy: phy-mtk-tphy: add support USB phys phy: phy-mtk-tphy: add support new version phy: phy-mtk-tphy: add a new reference clock xhci: mediatek: Add support for MTK xHCI host controller arm: dts: mt7629: add usb related nodes dt-bindings: phy-mtk-tphy: add properties of address mapping and clocks dt-bindings: usb: mtk-xhci: Add binding for MediaTek xHCI host controller MAINTAINERS: MediaTek: add USB related files
MAINTAINERS | 3 + arch/arm/dts/mt7629-rfb.dts | 8 + arch/arm/dts/mt7629.dtsi | 41 +++ arch/sandbox/dts/test.dts | 29 ++ doc/device-tree-bindings/phy/phy-mtk-tphy.txt | 78 ++++- .../usb/mediatek,mtk-xhci.txt | 40 +++ drivers/core/ofnode.c | 11 + drivers/core/read.c | 5 + drivers/phy/phy-mtk-tphy.c | 316 +++++++++++++++++- drivers/phy/phy-uclass.c | 97 ++++++ drivers/usb/dwc3/core.c | 87 +---- drivers/usb/dwc3/dwc3-generic.c | 7 +- drivers/usb/gadget/dwc2_udc_otg.c | 93 +----- drivers/usb/host/Kconfig | 6 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-dwc3.c | 7 +- drivers/usb/host/xhci-mtk.c | 303 +++++++++++++++++ drivers/usb/host/xhci.c | 10 + include/dm/ofnode.h | 8 + include/dm/read.h | 13 + include/dwc3-uboot.h | 11 +- include/generic-phy.h | 92 +++++ include/usb/xhci.h | 3 + test/dm/ofnode.c | 21 ++ test/dm/phy.c | 33 ++ 25 files changed, 1135 insertions(+), 188 deletions(-) create mode 100644 doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt create mode 100644 drivers/usb/host/xhci-mtk.c

From: Chunfeng Yun chunfeng.yun@mediatek.com
This patch add function used to get the child count of a ofnode or a device
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com Reviewed-by: Simon Glass sjg@chromium.org --- v8: no changes v7: no changes
v6: add Reviewed-by Weijie & Simon
v4~v5: no changes
v3: 1. add non/inline function dev_get_child_count() instead of macro suggested by Simon
v2: 1. move ofnode_get_child_count() into ofnode.c suggested by Simon 2. add a new macro dev_get_child_count() --- drivers/core/ofnode.c | 11 +++++++++++ drivers/core/read.c | 5 +++++ include/dm/ofnode.h | 8 ++++++++ include/dm/read.h | 13 +++++++++++++ 4 files changed, 37 insertions(+)
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 20871a6815..e3c42dae5c 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -474,6 +474,17 @@ ofnode ofnode_get_chosen_node(const char *name) return ofnode_path(prop); }
+int ofnode_get_child_count(ofnode parent) +{ + ofnode child; + int num = 0; + + ofnode_for_each_subnode(child, parent) + num++; + + return num; +} + static int decode_timing_property(ofnode node, const char *name, struct timing_entry *result) { diff --git a/drivers/core/read.c b/drivers/core/read.c index 47b8e03446..3d421f7a69 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -352,3 +352,8 @@ fdt_addr_t dev_read_addr_pci(const struct udevice *dev)
return addr; } + +int dev_get_child_count(const struct udevice *dev) +{ + return ofnode_get_child_count(dev_ofnode(dev)); +} diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 618fc10390..a0d3df7786 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -879,6 +879,14 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname, ofnode_valid(node); \ node = ofnode_next_subnode(node))
+/** + * ofnode_get_child_count() - get the child count of a ofnode + * + * @node: valid node to get its child count + * @return the number of subnodes + */ +int ofnode_get_child_count(ofnode parent); + /** * ofnode_translate_address() - Translate a device-tree address * diff --git a/include/dm/read.h b/include/dm/read.h index 03c15b8550..b952551d55 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -669,6 +669,14 @@ u64 dev_translate_dma_address(const struct udevice *dev, */ int dev_read_alias_highest_id(const char *stem);
+/** + * dev_get_child_count() - get the child count of a device + * + * @dev: device to use for interation (struct udevice *) + * @return the count of child subnode + */ +int dev_get_child_count(const struct udevice *dev); + #else /* CONFIG_DM_DEV_READ_INLINE is enabled */
static inline int dev_read_u32(const struct udevice *dev, @@ -978,6 +986,11 @@ static inline int dev_read_alias_highest_id(const char *stem) return fdtdec_get_alias_highest_id(gd->fdt_blob, stem); }
+static inline int dev_get_child_count(const struct udevice *dev) +{ + return ofnode_get_child_count(dev_ofnode(dev)); +} + #endif /* CONFIG_DM_DEV_READ_INLINE */
/**

From: Chunfeng Yun chunfeng.yun@mediatek.com
Add a test item for ofnode_get_child_count()
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: add @index for nodes with reg property suggested by Fabio
v6: add Reviewed-by Weijie
v4~v5: no changes
v3: 1. squash dts patch into this one suggested by Simon 2. add reviewed-by Simon
v2: a new patch to test ofnode_get_child_count() suggested by Simon --- arch/sandbox/dts/test.dts | 18 ++++++++++++++++++ test/dm/ofnode.c | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index df9f1835c9..f3f08da81e 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -218,6 +218,24 @@ compatible = "denx,u-boot-fdt-test1"; };
+ i-test { + compatible = "mediatek,u-boot-fdt-test"; + #address-cells = <1>; + #size-cells = <0>; + + subnode@0 { + reg = <0>; + }; + + subnode@1 { + reg = <1>; + }; + + subnode@2 { + reg = <2>; + }; + }; + devres-test { compatible = "denx,u-boot-devres-test"; }; diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 1c49eaf38b..07d5c7d7a6 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -113,3 +113,24 @@ static int dm_test_ofnode_read_chosen(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_read_chosen, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_ofnode_get_child_count(struct unit_test_state *uts) +{ + ofnode node, child_node; + u32 val; + + node = ofnode_path("/i-test"); + ut_assert(ofnode_valid(node)); + + val = ofnode_get_child_count(node); + ut_asserteq(3, val); + + child_node = ofnode_first_subnode(node); + ut_assert(ofnode_valid(child_node)); + val = ofnode_get_child_count(child_node); + ut_asserteq(0, val); + + return 0; +} +DM_TEST(dm_test_ofnode_get_child_count, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

From: Chunfeng Yun chunfeng.yun@mediatek.com
This patch adds a "bulk" API to the phy API in order to get/init/exit/power on/off a group of phys associated with a device.
The bulk API will avoid adding a copy of the same code to manage a group of phys in drivers.
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com Reviewed-by: Jagan Teki jagan@amarulasolutions.com --- v8: no changes
v7: provide bulk init/exit/power on/off separately suggested by Jagan
v6: add Reviewed-by Weijie
v5: no changes
v4: new patch --- drivers/phy/phy-uclass.c | 97 ++++++++++++++++++++++++++++++++++++++++ include/generic-phy.h | 92 +++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+)
diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c index e463b0b400..6ab78448af 100644 --- a/drivers/phy/phy-uclass.c +++ b/drivers/phy/phy-uclass.c @@ -6,6 +6,7 @@
#include <common.h> #include <dm.h> +#include <dm/devres.h> #include <generic-phy.h>
static inline struct phy_ops *phy_dev_ops(struct udevice *dev) @@ -167,6 +168,102 @@ int generic_phy_power_off(struct phy *phy) return ops->power_off ? ops->power_off(phy) : 0; }
+int generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk) +{ + int i, ret, count; + + bulk->count = 0; + + /* Return if no phy declared */ + if (!dev_read_prop(dev, "phys", NULL)) + return 0; + + count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); + if (count < 1) + return count; + + bulk->phys = devm_kcalloc(dev, count, sizeof(struct phy), GFP_KERNEL); + if (!bulk->phys) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = generic_phy_get_by_index(dev, i, &bulk->phys[i]); + if (ret) { + pr_err("Failed to get PHY%d for %s\n", i, dev->name); + return ret; + } + bulk->count++; + } + + return 0; +} + +int generic_phy_init_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = generic_phy_init(&phys[i]); + if (ret) { + pr_err("Can't init PHY%d\n", i); + goto phys_init_err; + } + } + + return 0; + +phys_init_err: + for (; i > 0; i--) + generic_phy_exit(&phys[i - 1]); + + return ret; +} + +int generic_phy_exit_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret = 0; + + for (i = 0; i < bulk->count; i++) + ret |= generic_phy_exit(&phys[i]); + + return ret; +} + +int generic_phy_power_on_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = generic_phy_power_on(&phys[i]); + if (ret) { + pr_err("Can't power on PHY%d\n", i); + goto phys_poweron_err; + } + } + + return 0; + +phys_poweron_err: + for (; i > 0; i--) + generic_phy_power_off(&phys[i - 1]); + + return ret; +} + +int generic_phy_power_off_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret = 0; + + for (i = 0; i < bulk->count; i++) + ret |= generic_phy_power_off(&phys[i]); + + return ret; +} + UCLASS_DRIVER(phy) = { .id = UCLASS_PHY, .name = "phy", diff --git a/include/generic-phy.h b/include/generic-phy.h index 73537025c2..55629ae0b4 100644 --- a/include/generic-phy.h +++ b/include/generic-phy.h @@ -124,6 +124,23 @@ struct phy_ops { int (*power_off)(struct phy *phy); };
+/** + * struct phy_bulk - A handle to (allowing control of) a bulk of phys. + * + * Consumers provide storage for the phy bulk. The content of the structure is + * managed solely by the phy API. A phy bulk struct is initialized + * by "get"ing the phy bulk struct. + * The phy bulk struct is passed to all other bulk phy APIs to apply + * the API to all the phy in the bulk struct. + * + * @phys: An array of phy handles. + * @count: The number of phy handles in the phys array. + */ +struct phy_bulk { + struct phy *phys; + unsigned int count; +}; + #ifdef CONFIG_PHY
/** @@ -250,6 +267,55 @@ int generic_phy_get_by_node(ofnode node, int index, struct phy *phy); int generic_phy_get_by_name(struct udevice *user, const char *phy_name, struct phy *phy);
+/** + * generic_phy_get_bulk - Get all phys of a device. + * + * This looks up and gets all phys of the consumer device; each device is + * assumed to have n phys associated with it somehow, and this function finds + * and gets all of them in a separate structure. + * + * @dev: The consumer device. + * @bulk A pointer to a phy bulk struct to initialize. + * @return 0 if OK, or a negative error code. + */ +int generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk); + +/** + * generic_phy_init_bulk() - Initialize all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_init_bulk(struct phy_bulk *bulk); + +/** + * generic_phy_exit_bulk() - de-initialize all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_exit_bulk(struct phy_bulk *bulk); + +/** + * generic_phy_power_on_bulk() - Power on all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_power_on_bulk(struct phy_bulk *bulk); + +/** + * generic_phy_power_off_bulk() - Power off all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_power_off_bulk(struct phy_bulk *bulk); + #else /* CONFIG_PHY */
static inline int generic_phy_init(struct phy *phy) @@ -289,6 +355,32 @@ static inline int generic_phy_get_by_name(struct udevice *user, const char *phy_ return 0; }
+static inline int +generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_init_bulk(struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_exit_bulk(struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_power_on_bulk(struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_power_off_bulk(struct phy_bulk *bulk) +{ + return 0; +} + #endif /* CONFIG_PHY */
/**

From: Chunfeng Yun chunfeng.yun@mediatek.com
Add a test item for the phy_bulk API
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Jagan Teki jagan@amarulasolutions.com --- v8: add Reviewed-by Jagan
v7: test new API
v6: add Reviewed-by Weijie & Simon
v5: no changes
v4: new patch --- arch/sandbox/dts/test.dts | 11 +++++++++++ test/dm/phy.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index f3f08da81e..ef04b8e2f7 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -159,12 +159,23 @@ broken; };
+ phy_provider2: gen_phy@2 { + compatible = "sandbox,phy"; + #phy-cells = <0>; + }; + gen_phy_user: gen_phy_user { compatible = "simple-bus"; phys = <&phy_provider0 0>, <&phy_provider0 1>, <&phy_provider1>; phy-names = "phy1", "phy2", "phy3"; };
+ gen_phy_user1: gen_phy_user1 { + compatible = "simple-bus"; + phys = <&phy_provider0 0>, <&phy_provider2>; + phy-names = "phy1", "phy2"; + }; + some-bus { #address-cells = <1>; #size-cells = <0>; diff --git a/test/dm/phy.c b/test/dm/phy.c index 21d92194b9..92455d94af 100644 --- a/test/dm/phy.c +++ b/test/dm/phy.c @@ -110,3 +110,36 @@ static int dm_test_phy_ops(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_phy_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_phy_bulk(struct unit_test_state *uts) +{ + struct phy_bulk phys; + struct udevice *parent; + + /* test normal operations */ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user1", &parent)); + + ut_assertok(generic_phy_get_bulk(parent, &phys)); + ut_asserteq(2, phys.count); + + ut_asserteq(0, generic_phy_init_bulk(&phys)); + ut_asserteq(0, generic_phy_power_on_bulk(&phys)); + ut_asserteq(0, generic_phy_power_off_bulk(&phys)); + ut_asserteq(0, generic_phy_exit_bulk(&phys)); + + /* has a known problem phy */ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + + ut_assertok(generic_phy_get_bulk(parent, &phys)); + ut_asserteq(3, phys.count); + + ut_asserteq(0, generic_phy_init_bulk(&phys)); + ut_asserteq(-EIO, generic_phy_power_on_bulk(&phys)); + ut_asserteq(-EIO, generic_phy_power_off_bulk(&phys)); + ut_asserteq(0, generic_phy_exit_bulk(&phys)); + + return 0; +} +DM_TEST(dm_test_phy_bulk, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

From: Chunfeng Yun chunfeng.yun@mediatek.com
Get a group of phys by the phy bulk API
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: fix build-error for xilinx-board
v7: use new API of phy bulk
v6: add Reviewed-by Weijie
v5: no changes
v4: new patch --- drivers/usb/dwc3/core.c | 87 ++++++--------------------------- drivers/usb/dwc3/dwc3-generic.c | 7 ++- drivers/usb/host/xhci-dwc3.c | 7 ++- include/dwc3-uboot.h | 11 ++--- 4 files changed, 27 insertions(+), 85 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 4ec3f6df6a..a8982bdc09 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -838,87 +838,32 @@ MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys) +int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) { - int i, ret, count; - struct phy *usb_phys; - - /* Return if no phy declared */ - if (!dev_read_prop(dev, "phys", NULL)) - return 0; - count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); - if (count <= 0) - return count; - - usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), - GFP_KERNEL); - if (!usb_phys) - return -ENOMEM; - - for (i = 0; i < count; i++) { - ret = generic_phy_get_by_index(dev, i, &usb_phys[i]); - if (ret && ret != -ENOENT) { - pr_err("Failed to get USB PHY%d for %s\n", - i, dev->name); - return ret; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_init(&usb_phys[i]); - if (ret) { - pr_err("Can't init USB PHY%d for %s\n", - i, dev->name); - goto phys_init_err; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_power_on(&usb_phys[i]); - if (ret) { - pr_err("Can't power USB PHY%d for %s\n", - i, dev->name); - goto phys_poweron_err; - } - } - - *array = usb_phys; - *num_phys = count; - return 0; - -phys_poweron_err: - for (i = count - 1; i >= 0; i--) - generic_phy_power_off(&usb_phys[i]); + int ret;
- for (i = 0; i < count; i++) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret;
- return ret; + ret = generic_phy_init_bulk(phys); + if (ret) + return ret;
-phys_init_err: - for (; i >= 0; i--) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys);
return ret; }
-int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys) +int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) { - int i, ret; - - for (i = 0; i < num_phys; i++) { - if (!generic_phy_valid(&usb_phys[i])) - continue; - - ret = generic_phy_power_off(&usb_phys[i]); - ret |= generic_phy_exit(&usb_phys[i]); - if (ret) { - pr_err("Can't shutdown USB PHY%d for %s\n", - i, dev->name); - } - } + int ret;
- return 0; + ret = generic_phy_power_off_bulk(phys); + ret |= generic_phy_exit_bulk(phys); + return ret; } #endif
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index febcfc0f54..eabd53a36d 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -33,8 +33,7 @@ struct dwc3_generic_plat { struct dwc3_generic_priv { void *base; struct dwc3 dwc3; - struct phy *phys; - int num_phys; + struct phy_bulk phys; };
struct dwc3_generic_host_priv { @@ -56,7 +55,7 @@ static int dwc3_generic_probe(struct udevice *dev, dwc3_of_parse(dwc3); #endif
- rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys); + rc = dwc3_setup_phy(dev, &priv->phys); if (rc) return rc;
@@ -79,7 +78,7 @@ static int dwc3_generic_remove(struct udevice *dev, struct dwc3 *dwc3 = &priv->dwc3;
dwc3_remove(dwc3); - dwc3_shutdown_phy(dev, priv->phys, priv->num_phys); + dwc3_shutdown_phy(dev, &priv->phys); unmap_physmem(dwc3->regs, MAP_NOCACHE);
return 0; diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index 9fcfa39d4b..563db1a426 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -19,8 +19,7 @@ #include <linux/usb/otg.h>
struct xhci_dwc3_platdata { - struct phy *usb_phys; - int num_phys; + struct phy_bulk *usb_phys; };
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) @@ -125,7 +124,7 @@ static int xhci_dwc3_probe(struct udevice *dev) hcor = (struct xhci_hcor *)((uintptr_t)hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
- ret = dwc3_setup_phy(dev, &plat->usb_phys, &plat->num_phys); + ret = dwc3_setup_phy(dev, plat->usb_phys); if (ret && (ret != -ENOTSUPP)) return ret;
@@ -168,7 +167,7 @@ static int xhci_dwc3_remove(struct udevice *dev) { struct xhci_dwc3_platdata *plat = dev_get_platdata(dev);
- dwc3_shutdown_phy(dev, plat->usb_phys, plat->num_phys); + dwc3_shutdown_phy(dev, plat->usb_phys);
return xhci_deregister(dev); } diff --git a/include/dwc3-uboot.h b/include/dwc3-uboot.h index 3c9e204cf0..ce835fd1b2 100644 --- a/include/dwc3-uboot.h +++ b/include/dwc3-uboot.h @@ -9,6 +9,7 @@ #ifndef __DWC3_UBOOT_H_ #define __DWC3_UBOOT_H_
+#include <generic-phy.h> #include <linux/usb/otg.h> #include <linux/usb/phy.h>
@@ -43,17 +44,15 @@ void dwc3_uboot_handle_interrupt(int index);
struct phy; #if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys); -int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys); +int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys); +int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys); #else -static inline int dwc3_setup_phy(struct udevice *dev, struct phy **array, - int *num_phys) +static inline int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) { return -ENOTSUPP; }
-static inline int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, - int num_phys) +static inline int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) { return -ENOTSUPP; }

On Sat, 2020-05-02 at 11:35 +0200, Frank Wunderlich wrote:
From: Chunfeng Yun chunfeng.yun@mediatek.com
Get a group of phys by the phy bulk API
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com
v8: fix build-error for xilinx-board
v7: use new API of phy bulk
v6: add Reviewed-by Weijie
v5: no changes
v4: new patch
drivers/usb/dwc3/core.c | 87 ++++++--------------------------- drivers/usb/dwc3/dwc3-generic.c | 7 ++- drivers/usb/host/xhci-dwc3.c | 7 ++- include/dwc3-uboot.h | 11 ++--- 4 files changed, 27 insertions(+), 85 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 4ec3f6df6a..a8982bdc09 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -838,87 +838,32 @@ MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys) +int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) {
- int i, ret, count;
- struct phy *usb_phys;
- /* Return if no phy declared */
- if (!dev_read_prop(dev, "phys", NULL))
return 0;
- count = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
- if (count <= 0)
return count;
- usb_phys = devm_kcalloc(dev, count, sizeof(struct phy),
GFP_KERNEL);
- if (!usb_phys)
return -ENOMEM;
- for (i = 0; i < count; i++) {
ret = generic_phy_get_by_index(dev, i, &usb_phys[i]);
if (ret && ret != -ENOENT) {
pr_err("Failed to get USB PHY%d for %s\n",
i, dev->name);
return ret;
}
- }
- for (i = 0; i < count; i++) {
ret = generic_phy_init(&usb_phys[i]);
if (ret) {
pr_err("Can't init USB PHY%d for %s\n",
i, dev->name);
goto phys_init_err;
}
- }
- for (i = 0; i < count; i++) {
ret = generic_phy_power_on(&usb_phys[i]);
if (ret) {
pr_err("Can't power USB PHY%d for %s\n",
i, dev->name);
goto phys_poweron_err;
}
- }
- *array = usb_phys;
- *num_phys = count;
- return 0;
-phys_poweron_err:
- for (i = count - 1; i >= 0; i--)
generic_phy_power_off(&usb_phys[i]);
- int ret;
- for (i = 0; i < count; i++)
generic_phy_exit(&usb_phys[i]);
- ret = generic_phy_get_bulk(dev, phys);
- if (ret)
return ret;
- return ret;
- ret = generic_phy_init_bulk(phys);
- if (ret)
return ret;
-phys_init_err:
- for (; i >= 0; i--)
generic_phy_exit(&usb_phys[i]);
ret = generic_phy_power_on_bulk(phys);
if (ret)
generic_phy_exit_bulk(phys);
return ret;
}
-int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys) +int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) {
- int i, ret;
- for (i = 0; i < num_phys; i++) {
if (!generic_phy_valid(&usb_phys[i]))
continue;
ret = generic_phy_power_off(&usb_phys[i]);
ret |= generic_phy_exit(&usb_phys[i]);
if (ret) {
pr_err("Can't shutdown USB PHY%d for %s\n",
i, dev->name);
}
- }
- int ret;
- return 0;
- ret = generic_phy_power_off_bulk(phys);
- ret |= generic_phy_exit_bulk(phys);
- return ret;
} #endif
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index febcfc0f54..eabd53a36d 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -33,8 +33,7 @@ struct dwc3_generic_plat { struct dwc3_generic_priv { void *base; struct dwc3 dwc3;
- struct phy *phys;
- int num_phys;
- struct phy_bulk phys;
};
struct dwc3_generic_host_priv { @@ -56,7 +55,7 @@ static int dwc3_generic_probe(struct udevice *dev, dwc3_of_parse(dwc3); #endif
- rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys);
- rc = dwc3_setup_phy(dev, &priv->phys); if (rc) return rc;
@@ -79,7 +78,7 @@ static int dwc3_generic_remove(struct udevice *dev, struct dwc3 *dwc3 = &priv->dwc3;
dwc3_remove(dwc3);
- dwc3_shutdown_phy(dev, priv->phys, priv->num_phys);
dwc3_shutdown_phy(dev, &priv->phys); unmap_physmem(dwc3->regs, MAP_NOCACHE);
return 0;
diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index 9fcfa39d4b..563db1a426 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -19,8 +19,7 @@ #include <linux/usb/otg.h>
struct xhci_dwc3_platdata {
- struct phy *usb_phys;
- int num_phys;
- struct phy_bulk *usb_phys;
I cant access github, so don't know why you build fail, but I build pass https://gitlab.denx.de/u-boot/custodians/u-boot-usb/pipelines/3054
here *usb_phys is not allocated before used, this is why I usb "struct phy_bulk usb_phys".
Would you please help to test the function on xilinx-board? due to I just build it pass, no board to test it.
Thank you for sending out the v8 series of pathes
};
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) @@ -125,7 +124,7 @@ static int xhci_dwc3_probe(struct udevice *dev) hcor = (struct xhci_hcor *)((uintptr_t)hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
- ret = dwc3_setup_phy(dev, &plat->usb_phys, &plat->num_phys);
- ret = dwc3_setup_phy(dev, plat->usb_phys); if (ret && (ret != -ENOTSUPP)) return ret;
@@ -168,7 +167,7 @@ static int xhci_dwc3_remove(struct udevice *dev) { struct xhci_dwc3_platdata *plat = dev_get_platdata(dev);
- dwc3_shutdown_phy(dev, plat->usb_phys, plat->num_phys);
dwc3_shutdown_phy(dev, plat->usb_phys);
return xhci_deregister(dev);
} diff --git a/include/dwc3-uboot.h b/include/dwc3-uboot.h index 3c9e204cf0..ce835fd1b2 100644 --- a/include/dwc3-uboot.h +++ b/include/dwc3-uboot.h @@ -9,6 +9,7 @@ #ifndef __DWC3_UBOOT_H_ #define __DWC3_UBOOT_H_
+#include <generic-phy.h> #include <linux/usb/otg.h> #include <linux/usb/phy.h>
@@ -43,17 +44,15 @@ void dwc3_uboot_handle_interrupt(int index);
struct phy; #if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys); -int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys); +int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys); +int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys); #else -static inline int dwc3_setup_phy(struct udevice *dev, struct phy **array,
int *num_phys)
+static inline int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) { return -ENOTSUPP; }
-static inline int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys,
int num_phys)
+static inline int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) { return -ENOTSUPP; }

Am 6. Mai 2020 05:09:55 MESZ schrieb Chunfeng Yun chunfeng.yun@mediatek.com:
Would you please help to test the function on xilinx-board? due to I just build it pass, no board to test it.
Thank you for sending out the v8 series of pathes
Hi,
I also only fixed the build-error (to get series merged into 2020-07 for further work)...have no such board...sorry for wrong fix...have you found way to reproduce the build-error? It's already in mareks tree and he send pull-request. I hope tom merges it to master and you can send a fix only (instead of full series which need all work again). regards Frank

On Wed, 2020-05-06 at 09:35 +0200, Frank Wunderlich wrote:
Am 6. Mai 2020 05:09:55 MESZ schrieb Chunfeng Yun chunfeng.yun@mediatek.com:
Would you please help to test the function on xilinx-board? due to I just build it pass, no board to test it.
Thank you for sending out the v8 series of pathes
Hi,
I also only fixed the build-error (to get series merged into 2020-07 for further work)...have no such board...sorry for wrong fix...have you found way to reproduce the build-error?
Yes, I can reproduce it, will send a fix patch for it, thanks
It's already in mareks tree and he send pull-request. I hope tom merges it to master and you can send a fix only (instead of full series which need all work again). regards Frank
Sorry, I found that I loss a changed file when prepare this patch, because I make a new branch for it:(

From: Chunfeng Yun chunfeng.yun@mediatek.com
Use the phy bulk API to get a group of phys
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: use new API of phy bulk
v6: add Reviewed-by Weijie
v5: no changes
v4: new patch --- drivers/usb/gadget/dwc2_udc_otg.c | 93 ++++++------------------------- 1 file changed, 17 insertions(+), 76 deletions(-)
diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index b9c814cf73..9f21af2923 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -943,8 +943,7 @@ int usb_gadget_handle_interrupts(int index) struct dwc2_priv_data { struct clk_bulk clks; struct reset_ctl_bulk resets; - struct phy *phys; - int num_phys; + struct phy_bulk phys; struct udevice *usb33d_supply; };
@@ -953,87 +952,29 @@ int dm_usb_gadget_handle_interrupts(struct udevice *dev) return dwc2_udc_handle_interrupt(); }
-int dwc2_phy_setup(struct udevice *dev, struct phy **array, int *num_phys) +static int dwc2_phy_setup(struct udevice *dev, struct phy_bulk *phys) { - int i, ret, count; - struct phy *usb_phys; - - /* Return if no phy declared */ - if (!dev_read_prop(dev, "phys", NULL)) - return 0; - - count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); - if (count <= 0) - return count; - - usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), - GFP_KERNEL); - if (!usb_phys) - return -ENOMEM; - - for (i = 0; i < count; i++) { - ret = generic_phy_get_by_index(dev, i, &usb_phys[i]); - if (ret && ret != -ENOENT) { - dev_err(dev, "Failed to get USB PHY%d for %s\n", - i, dev->name); - return ret; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_init(&usb_phys[i]); - if (ret) { - dev_err(dev, "Can't init USB PHY%d for %s\n", - i, dev->name); - goto phys_init_err; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_power_on(&usb_phys[i]); - if (ret) { - dev_err(dev, "Can't power USB PHY%d for %s\n", - i, dev->name); - goto phys_poweron_err; - } - } - - *array = usb_phys; - *num_phys = count; - - return 0; - -phys_poweron_err: - for (i = count - 1; i >= 0; i--) - generic_phy_power_off(&usb_phys[i]); + int ret;
- for (i = 0; i < count; i++) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret;
- return ret; + ret = generic_phy_init_bulk(phys); + if (ret) + return ret;
-phys_init_err: - for (; i >= 0; i--) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys);
return ret; }
-void dwc2_phy_shutdown(struct udevice *dev, struct phy *usb_phys, int num_phys) +static void dwc2_phy_shutdown(struct udevice *dev, struct phy_bulk *phys) { - int i, ret; - - for (i = 0; i < num_phys; i++) { - if (!generic_phy_valid(&usb_phys[i])) - continue; - - ret = generic_phy_power_off(&usb_phys[i]); - ret |= generic_phy_exit(&usb_phys[i]); - if (ret) { - dev_err(dev, "Can't shutdown USB PHY%d for %s\n", - i, dev->name); - } - } + generic_phy_power_off_bulk(phys); + generic_phy_exit_bulk(phys); }
static int dwc2_udc_otg_ofdata_to_platdata(struct udevice *dev) @@ -1158,7 +1099,7 @@ static int dwc2_udc_otg_probe(struct udevice *dev) if (ret) return ret;
- ret = dwc2_phy_setup(dev, &priv->phys, &priv->num_phys); + ret = dwc2_phy_setup(dev, &priv->phys); if (ret) return ret;
@@ -1208,7 +1149,7 @@ static int dwc2_udc_otg_remove(struct udevice *dev)
clk_release_bulk(&priv->clks);
- dwc2_phy_shutdown(dev, priv->phys, priv->num_phys); + dwc2_phy_shutdown(dev, &priv->phys);
return dm_scan_fdt_dev(dev); }

From: Chunfeng Yun chunfeng.yun@mediatek.com
Support USB2 and USB3 PHY with shared banks when support multi-phys
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v3~v5: no changes
v2: get the count of phys by dev_get_child_count() --- drivers/phy/phy-mtk-tphy.c | 227 +++++++++++++++++++++++++++++++++++-- 1 file changed, 218 insertions(+), 9 deletions(-)
diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c index bd089b7a43..71bc706c6e 100644 --- a/drivers/phy/phy-mtk-tphy.c +++ b/drivers/phy/phy-mtk-tphy.c @@ -20,11 +20,66 @@ /* version V1 sub-banks offset base address */ /* banks shared by multiple phys */ #define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ +#define SSUSB_SIFSLV_V1_U2FREQ 0x100 /* shared by u2 phys */ #define SSUSB_SIFSLV_V1_CHIP 0x300 /* shared by u3 phys */ +/* u2 phy bank */ +#define SSUSB_SIFSLV_V1_U2PHY_COM 0x000 /* u3/pcie/sata phy banks */ #define SSUSB_SIFSLV_V1_U3PHYD 0x000 #define SSUSB_SIFSLV_V1_U3PHYA 0x200
+#define U3P_USBPHYACR0 0x000 +#define PA0_RG_U2PLL_FORCE_ON BIT(15) +#define PA0_RG_USB20_INTR_EN BIT(5) + +#define U3P_USBPHYACR5 0x014 +#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15) +#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12) +#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) +#define PA5_RG_U2_HS_100U_U3_EN BIT(11) + +#define U3P_USBPHYACR6 0x018 +#define PA6_RG_U2_BC11_SW_EN BIT(23) +#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20) +#define PA6_RG_U2_SQTH GENMASK(3, 0) +#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x)) + +#define U3P_U2PHYACR4 0x020 +#define P2C_RG_USB20_GPIO_CTL BIT(9) +#define P2C_USB20_GPIO_MODE BIT(8) +#define P2C_U2_GPIO_CTR_MSK \ + (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE) + +#define U3P_U2PHYDTM0 0x068 +#define P2C_FORCE_UART_EN BIT(26) +#define P2C_FORCE_DATAIN BIT(23) +#define P2C_FORCE_DM_PULLDOWN BIT(21) +#define P2C_FORCE_DP_PULLDOWN BIT(20) +#define P2C_FORCE_XCVRSEL BIT(19) +#define P2C_FORCE_SUSPENDM BIT(18) +#define P2C_FORCE_TERMSEL BIT(17) +#define P2C_RG_DATAIN GENMASK(13, 10) +#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10) +#define P2C_RG_DMPULLDOWN BIT(7) +#define P2C_RG_DPPULLDOWN BIT(6) +#define P2C_RG_XCVRSEL GENMASK(5, 4) +#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4) +#define P2C_RG_SUSPENDM BIT(3) +#define P2C_RG_TERMSEL BIT(2) +#define P2C_DTM0_PART_MASK \ + (P2C_FORCE_DATAIN | P2C_FORCE_DM_PULLDOWN | \ + P2C_FORCE_DP_PULLDOWN | P2C_FORCE_XCVRSEL | \ + P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \ + P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL) + +#define U3P_U2PHYDTM1 0x06C +#define P2C_RG_UART_EN BIT(16) +#define P2C_FORCE_IDDIG BIT(9) +#define P2C_RG_VBUSVALID BIT(5) +#define P2C_RG_SESSEND BIT(4) +#define P2C_RG_AVALID BIT(2) +#define P2C_RG_IDDIG BIT(1) + #define U3P_U3_CHIP_GPIO_CTLD 0x0c #define P3C_REG_IP_SW_RST BIT(31) #define P3C_MCU_BUS_CK_GATE_EN BIT(30) @@ -42,6 +97,14 @@ #define P3A_RG_CLKDRV_AMP GENMASK(31, 29) #define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29)
+#define U3P_U3_PHYA_REG6 0x018 +#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28) +#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28) + +#define U3P_U3_PHYA_REG9 0x024 +#define P3A_RG_RX_DAC_MUX GENMASK(5, 1) +#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1) + #define U3P_U3_PHYA_DA_REG0 0x100 #define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16) #define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16) @@ -77,6 +140,16 @@ #define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0) #define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x))
+#define U3P_U3_PHYD_LFPS1 0x00c +#define P3D_RG_FWAKE_TH GENMASK(21, 16) +#define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16) + +#define U3P_U3_PHYD_CDR1 0x05c +#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) +#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24) +#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8) +#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8) + #define U3P_U3_PHYD_RXDET1 0x128 #define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) #define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9) @@ -85,6 +158,16 @@ #define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) #define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x))
+#define U3P_SPLLC_XTALCTL3 0x018 +#define XC3_RG_U3_XTAL_RX_PWD BIT(9) +#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8) + +struct u2phy_banks { + void __iomem *misc; + void __iomem *fmreg; + void __iomem *com; +}; + struct u3phy_banks { void __iomem *spllc; void __iomem *chip; @@ -95,21 +178,127 @@ struct u3phy_banks { struct mtk_phy_instance { void __iomem *port_base; const struct device_node *np; - - struct u3phy_banks u3_banks; + union { + struct u2phy_banks u2_banks; + struct u3phy_banks u3_banks; + };
/* reference clock of anolog phy */ struct clk ref_clk; u32 index; - u8 type; + u32 type; };
struct mtk_tphy { + struct udevice *dev; void __iomem *sif_base; struct mtk_phy_instance **phys; int nphys; };
+static void u2_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + + /* switch to USB function, and enable usb pll */ + clrsetbits_le32(u2_banks->com + U3P_U2PHYDTM0, + P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM, + P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0)); + + clrbits_le32(u2_banks->com + U3P_U2PHYDTM1, P2C_RG_UART_EN); + setbits_le32(u2_banks->com + U3P_USBPHYACR0, PA0_RG_USB20_INTR_EN); + + /* disable switch 100uA current to SSUSB */ + clrbits_le32(u2_banks->com + U3P_USBPHYACR5, PA5_RG_U2_HS_100U_U3_EN); + + clrbits_le32(u2_banks->com + U3P_U2PHYACR4, P2C_U2_GPIO_CTR_MSK); + + /* DP/DM BC1.1 path Disable */ + clrsetbits_le32(u2_banks->com + U3P_USBPHYACR6, + PA6_RG_U2_BC11_SW_EN | PA6_RG_U2_SQTH, + PA6_RG_U2_SQTH_VAL(2)); + + /* set HS slew rate */ + clrsetbits_le32(u2_banks->com + U3P_USBPHYACR5, + PA5_RG_U2_HSTX_SRCTRL, PA5_RG_U2_HSTX_SRCTRL_VAL(4)); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + +static void u2_phy_instance_power_on(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + + clrbits_le32(u2_banks->com + U3P_U2PHYDTM0, + P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK); + + /* OTG Enable */ + setbits_le32(u2_banks->com + U3P_USBPHYACR6, + PA6_RG_U2_OTG_VBUSCMP_EN); + + clrsetbits_le32(u2_banks->com + U3P_U2PHYDTM1, + P2C_RG_SESSEND, P2C_RG_VBUSVALID | P2C_RG_AVALID); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + +static void u2_phy_instance_power_off(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + + clrbits_le32(u2_banks->com + U3P_U2PHYDTM0, + P2C_RG_XCVRSEL | P2C_RG_DATAIN); + + /* OTG Disable */ + clrbits_le32(u2_banks->com + U3P_USBPHYACR6, + PA6_RG_U2_OTG_VBUSCMP_EN); + + clrsetbits_le32(u2_banks->com + U3P_U2PHYDTM1, + P2C_RG_VBUSVALID | P2C_RG_AVALID, P2C_RG_SESSEND); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + +static void u3_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + + /* gating PCIe Analog XTAL clock */ + setbits_le32(u3_banks->spllc + U3P_SPLLC_XTALCTL3, + XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD); + + /* gating XSQ */ + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG0, + P3A_RG_XTAL_EXT_EN_U3, P3A_RG_XTAL_EXT_EN_U3_VAL(2)); + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_REG9, + P3A_RG_RX_DAC_MUX, P3A_RG_RX_DAC_MUX_VAL(4)); + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_REG6, + P3A_RG_TX_EIDLE_CM, P3A_RG_TX_EIDLE_CM_VAL(0xe)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_CDR1, + P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1, + P3D_RG_CDR_BIR_LTD0_VAL(0xc) | + P3D_RG_CDR_BIR_LTD1_VAL(0x3)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_LFPS1, + P3D_RG_FWAKE_TH, P3D_RG_FWAKE_TH_VAL(0x34)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_RXDET1, + P3D_RG_RXDET_STB2_SET, P3D_RG_RXDET_STB2_SET_VAL(0x10)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_RXDET2, + P3D_RG_RXDET_STB2_SET_P3, + P3D_RG_RXDET_STB2_SET_P3_VAL(0x10)); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + static void pcie_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { @@ -187,9 +376,16 @@ static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, static void phy_v1_banks_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { + struct u2phy_banks *u2_banks = &instance->u2_banks; struct u3phy_banks *u3_banks = &instance->u3_banks;
switch (instance->type) { + case PHY_TYPE_USB2: + u2_banks->misc = NULL; + u2_banks->fmreg = tphy->sif_base + SSUSB_SIFSLV_V1_U2FREQ; + u2_banks->com = instance->port_base + SSUSB_SIFSLV_V1_U2PHY_COM; + break; + case PHY_TYPE_USB3: case PHY_TYPE_PCIE: u3_banks->spllc = tphy->sif_base + SSUSB_SIFSLV_V1_SPLLC; u3_banks->chip = tphy->sif_base + SSUSB_SIFSLV_V1_CHIP; @@ -197,6 +393,7 @@ static void phy_v1_banks_init(struct mtk_tphy *tphy, u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA; break; default: + dev_err(tphy->dev, "incompatible PHY type\n"); return; } } @@ -212,10 +409,17 @@ static int mtk_phy_init(struct phy *phy) return ret;
switch (instance->type) { + case PHY_TYPE_USB2: + u2_phy_instance_init(tphy, instance); + break; + case PHY_TYPE_USB3: + u3_phy_instance_init(tphy, instance); + break; case PHY_TYPE_PCIE: pcie_phy_instance_init(tphy, instance); break; default: + dev_err(tphy->dev, "incompatible PHY type\n"); return -EINVAL; }
@@ -227,7 +431,10 @@ static int mtk_phy_power_on(struct phy *phy) struct mtk_tphy *tphy = dev_get_priv(phy->dev); struct mtk_phy_instance *instance = tphy->phys[phy->id];
- pcie_phy_instance_power_on(tphy, instance); + if (instance->type == PHY_TYPE_USB2) + u2_phy_instance_power_on(tphy, instance); + else if (instance->type == PHY_TYPE_PCIE) + pcie_phy_instance_power_on(tphy, instance);
return 0; } @@ -237,7 +444,10 @@ static int mtk_phy_power_off(struct phy *phy) struct mtk_tphy *tphy = dev_get_priv(phy->dev); struct mtk_phy_instance *instance = tphy->phys[phy->id];
- pcie_phy_instance_power_off(tphy, instance); + if (instance->type == PHY_TYPE_USB2) + u2_phy_instance_power_off(tphy, instance); + else if (instance->type == PHY_TYPE_PCIE) + pcie_phy_instance_power_off(tphy, instance);
return 0; } @@ -285,8 +495,7 @@ static int mtk_phy_xlate(struct phy *phy, instance->type = args->args[1]; if (!(instance->type == PHY_TYPE_USB2 || instance->type == PHY_TYPE_USB3 || - instance->type == PHY_TYPE_PCIE || - instance->type == PHY_TYPE_SATA)) { + instance->type == PHY_TYPE_PCIE)) { dev_err(phy->dev, "unsupported device type\n"); return -EINVAL; } @@ -310,14 +519,14 @@ static int mtk_tphy_probe(struct udevice *dev) ofnode subnode; int index = 0;
- dev_for_each_subnode(subnode, dev) - tphy->nphys++; + tphy->nphys = dev_get_child_count(dev);
tphy->phys = devm_kcalloc(dev, tphy->nphys, sizeof(*tphy->phys), GFP_KERNEL); if (!tphy->phys) return -ENOMEM;
+ tphy->dev = dev; tphy->sif_base = dev_read_addr_ptr(dev); if (!tphy->sif_base) return -ENOENT;

From: Chunfeng Yun chunfeng.yun@mediatek.com
The new version removes all shared banks between multi-phys
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v2~v5: no changes --- drivers/phy/phy-mtk-tphy.c | 68 +++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c index 71bc706c6e..20167fe7cb 100644 --- a/drivers/phy/phy-mtk-tphy.c +++ b/drivers/phy/phy-mtk-tphy.c @@ -28,6 +28,17 @@ #define SSUSB_SIFSLV_V1_U3PHYD 0x000 #define SSUSB_SIFSLV_V1_U3PHYA 0x200
+/* version V2 sub-banks offset base address */ +/* u2 phy banks */ +#define SSUSB_SIFSLV_V2_MISC 0x000 +#define SSUSB_SIFSLV_V2_U2FREQ 0x100 +#define SSUSB_SIFSLV_V2_U2PHY_COM 0x300 +/* u3/pcie/sata phy banks */ +#define SSUSB_SIFSLV_V2_SPLLC 0x000 +#define SSUSB_SIFSLV_V2_CHIP 0x100 +#define SSUSB_SIFSLV_V2_U3PHYD 0x200 +#define SSUSB_SIFSLV_V2_U3PHYA 0x400 + #define U3P_USBPHYACR0 0x000 #define PA0_RG_U2PLL_FORCE_ON BIT(15) #define PA0_RG_USB20_INTR_EN BIT(5) @@ -162,6 +173,11 @@ #define XC3_RG_U3_XTAL_RX_PWD BIT(9) #define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
+enum mtk_phy_version { + MTK_TPHY_V1 = 1, + MTK_TPHY_V2, +}; + struct u2phy_banks { void __iomem *misc; void __iomem *fmreg; @@ -192,6 +208,7 @@ struct mtk_phy_instance { struct mtk_tphy { struct udevice *dev; void __iomem *sif_base; + enum mtk_phy_version version; struct mtk_phy_instance **phys; int nphys; }; @@ -304,6 +321,9 @@ static void pcie_phy_instance_init(struct mtk_tphy *tphy, { struct u3phy_banks *u3_banks = &instance->u3_banks;
+ if (tphy->version != MTK_TPHY_V1) + return; + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG0, P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H, P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | @@ -398,6 +418,31 @@ static void phy_v1_banks_init(struct mtk_tphy *tphy, } }
+static void phy_v2_banks_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + struct u3phy_banks *u3_banks = &instance->u3_banks; + + switch (instance->type) { + case PHY_TYPE_USB2: + u2_banks->misc = instance->port_base + SSUSB_SIFSLV_V2_MISC; + u2_banks->fmreg = instance->port_base + SSUSB_SIFSLV_V2_U2FREQ; + u2_banks->com = instance->port_base + SSUSB_SIFSLV_V2_U2PHY_COM; + break; + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: + u3_banks->spllc = instance->port_base + SSUSB_SIFSLV_V2_SPLLC; + u3_banks->chip = instance->port_base + SSUSB_SIFSLV_V2_CHIP; + u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V2_U3PHYD; + u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V2_U3PHYA; + break; + default: + dev_err(tphy->dev, "incompatible PHY type\n"); + return; + } +} + static int mtk_phy_init(struct phy *phy) { struct mtk_tphy *tphy = dev_get_priv(phy->dev); @@ -500,7 +545,14 @@ static int mtk_phy_xlate(struct phy *phy, return -EINVAL; }
- phy_v1_banks_init(tphy, instance); + if (tphy->version == MTK_TPHY_V1) { + phy_v1_banks_init(tphy, instance); + } else if (tphy->version == MTK_TPHY_V2) { + phy_v2_banks_init(tphy, instance); + } else { + dev_err(phy->dev, "phy version is not supported\n"); + return -EINVAL; + }
return 0; } @@ -527,9 +579,14 @@ static int mtk_tphy_probe(struct udevice *dev) return -ENOMEM;
tphy->dev = dev; - tphy->sif_base = dev_read_addr_ptr(dev); - if (!tphy->sif_base) - return -ENOENT; + tphy->version = dev_get_driver_data(dev); + + /* v1 has shared banks */ + if (tphy->version == MTK_TPHY_V1) { + tphy->sif_base = dev_read_addr_ptr(dev); + if (!tphy->sif_base) + return -ENOENT; + }
dev_for_each_subnode(subnode, dev) { struct mtk_phy_instance *instance; @@ -560,7 +617,8 @@ static int mtk_tphy_probe(struct udevice *dev) }
static const struct udevice_id mtk_tphy_id_table[] = { - { .compatible = "mediatek,generic-tphy-v1", }, + { .compatible = "mediatek,generic-tphy-v1", .data = MTK_TPHY_V1, }, + { .compatible = "mediatek,generic-tphy-v2", .data = MTK_TPHY_V2, }, { } };

From: Chunfeng Yun chunfeng.yun@mediatek.com
Usually the digital and analog phys use the same reference clock, but some platforms have two separate reference clocks for each of them, so add another optional clock to support them. In order to keep the clock names consistent with PHY IP's, change the da_ref for analog phy and ref clock for digital phy.
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v2~v5: no changes --- drivers/phy/phy-mtk-tphy.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c index 20167fe7cb..81525a48b7 100644 --- a/drivers/phy/phy-mtk-tphy.c +++ b/drivers/phy/phy-mtk-tphy.c @@ -199,8 +199,8 @@ struct mtk_phy_instance { struct u3phy_banks u3_banks; };
- /* reference clock of anolog phy */ - struct clk ref_clk; + struct clk ref_clk; /* reference clock of (digital) phy */ + struct clk da_ref_clk; /* reference clock of analog phy */ u32 index; u32 type; }; @@ -450,8 +450,17 @@ static int mtk_phy_init(struct phy *phy) int ret;
ret = clk_enable(&instance->ref_clk); - if (ret) + if (ret < 0) { + dev_err(tphy->dev, "failed to enable ref_clk\n"); return ret; + } + + ret = clk_enable(&instance->da_ref_clk); + if (ret < 0) { + dev_err(tphy->dev, "failed to enable da_ref_clk %d\n", ret); + clk_disable(&instance->ref_clk); + return ret; + }
switch (instance->type) { case PHY_TYPE_USB2: @@ -502,6 +511,7 @@ static int mtk_phy_exit(struct phy *phy) struct mtk_tphy *tphy = dev_get_priv(phy->dev); struct mtk_phy_instance *instance = tphy->phys[phy->id];
+ clk_disable(&instance->da_ref_clk); clk_disable(&instance->ref_clk);
return 0; @@ -611,6 +621,11 @@ static int mtk_tphy_probe(struct udevice *dev) &instance->ref_clk); if (err) return err; + + err = clk_get_optional_nodev(subnode, "da_ref", + &instance->da_ref_clk); + if (err) + return err; }
return 0;

From: Chunfeng Yun chunfeng.yun@mediatek.com
This patch is used to support the on-chip xHCI controller on MediaTek SoCs, currently control/bulk/interrupt transfers are supported.
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com Reviewed-by: Jagan Teki jagan@amarulasolutions.com --- v8: 1: mark xhci_mtk_phy_shutdown and xhci_mtk_phy_setup as static as suggested by Jagan Teki 2: add Reviewed-by Jagan
v7: use new API of phy bulk
v6: add Reviewed-by Weijie
v5: 1. print error number suggested by Marek 2. support interrupt transfer
v4: 1. use phy_bulk API
v3: 1. use macro approach to access registers suggested by Marek
v2: 1. use clk_bulk to get clocks suggested by Marek 2. use clrsetbits_le32() etc suggeseted by Marek --- drivers/usb/host/Kconfig | 6 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-mtk.c | 303 ++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 10 ++ include/usb/xhci.h | 3 + 5 files changed, 323 insertions(+) create mode 100644 drivers/usb/host/xhci-mtk.c
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 94ac969058..2f381dc958 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -30,6 +30,12 @@ config USB_XHCI_DWC3_OF_SIMPLE Support USB2/3 functionality in simple SoC integrations with USB controller based on the DesignWare USB3 IP Core.
+config USB_XHCI_MTK + bool "Support for MediaTek on-chip xHCI USB controller" + depends on ARCH_MEDIATEK + help + Enables support for the on-chip xHCI controller on MediaTek SoCs. + config USB_XHCI_MVEBU bool "MVEBU USB 3.0 support" default y diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index b62f346094..e8e3b17e42 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_USB_XHCI_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_XHCI_ROCKCHIP) += xhci-rockchip.o obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o +obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c new file mode 100644 index 0000000000..8ff71854fc --- /dev/null +++ b/drivers/usb/host/xhci-mtk.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 MediaTek, Inc. + * Authors: Chunfeng Yun chunfeng.yun@mediatek.com + */ + +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <dm/devres.h> +#include <generic-phy.h> +#include <malloc.h> +#include <usb.h> +#include <linux/errno.h> +#include <linux/compat.h> +#include <power/regulator.h> +#include <linux/iopoll.h> +#include <usb/xhci.h> + +/* IPPC (IP Port Control) registers */ +#define IPPC_IP_PW_CTRL0 0x00 +#define CTRL0_IP_SW_RST BIT(0) + +#define IPPC_IP_PW_CTRL1 0x04 +#define CTRL1_IP_HOST_PDN BIT(0) + +#define IPPC_IP_PW_STS1 0x10 +#define STS1_IP_SLEEP_STS BIT(30) +#define STS1_U3_MAC_RST BIT(16) +#define STS1_XHCI_RST BIT(11) +#define STS1_SYS125_RST BIT(10) +#define STS1_REF_RST BIT(8) +#define STS1_SYSPLL_STABLE BIT(0) + +#define IPPC_IP_XHCI_CAP 0x24 +#define CAP_U3_PORT_NUM(p) ((p) & 0xff) +#define CAP_U2_PORT_NUM(p) (((p) >> 8) & 0xff) + +#define IPPC_U3_CTRL_0P 0x30 +#define CTRL_U3_PORT_HOST_SEL BIT(2) +#define CTRL_U3_PORT_PDN BIT(1) +#define CTRL_U3_PORT_DIS BIT(0) + +#define IPPC_U2_CTRL_0P 0x50 +#define CTRL_U2_PORT_HOST_SEL BIT(2) +#define CTRL_U2_PORT_PDN BIT(1) +#define CTRL_U2_PORT_DIS BIT(0) + +#define IPPC_U3_CTRL(p) (IPPC_U3_CTRL_0P + ((p) * 0x08)) +#define IPPC_U2_CTRL(p) (IPPC_U2_CTRL_0P + ((p) * 0x08)) + +struct mtk_xhci { + struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ + struct xhci_hccr *hcd; + void __iomem *ippc; + struct udevice *dev; + struct udevice *vusb33_supply; + struct udevice *vbus_supply; + struct clk_bulk clks; + struct phy_bulk phys; + int num_u2ports; + int num_u3ports; +}; + +static int xhci_mtk_host_enable(struct mtk_xhci *mtk) +{ + u32 value; + u32 check_val; + int ret; + int i; + + /* power on host ip */ + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); + + /* power on and enable all u3 ports */ + for (i = 0; i < mtk->num_u3ports; i++) { + clrsetbits_le32(mtk->ippc + IPPC_U3_CTRL(i), + CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS, + CTRL_U3_PORT_HOST_SEL); + } + + /* power on and enable all u2 ports */ + for (i = 0; i < mtk->num_u2ports; i++) { + clrsetbits_le32(mtk->ippc + IPPC_U2_CTRL(i), + CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS, + CTRL_U2_PORT_HOST_SEL); + } + + /* + * wait for clocks to be stable, and clock domains reset to + * be inactive after power on and enable ports + */ + check_val = STS1_SYSPLL_STABLE | STS1_REF_RST | + STS1_SYS125_RST | STS1_XHCI_RST; + + if (mtk->num_u3ports) + check_val |= STS1_U3_MAC_RST; + + ret = readl_poll_timeout(mtk->ippc + IPPC_IP_PW_STS1, value, + (check_val == (value & check_val)), 20000); + if (ret) + dev_err(mtk->dev, "clocks are not stable 0x%x!\n", value); + + return ret; +} + +static int xhci_mtk_host_disable(struct mtk_xhci *mtk) +{ + int i; + + /* power down all u3 ports */ + for (i = 0; i < mtk->num_u3ports; i++) + setbits_le32(mtk->ippc + IPPC_U3_CTRL(i), CTRL_U3_PORT_PDN); + + /* power down all u2 ports */ + for (i = 0; i < mtk->num_u2ports; i++) + setbits_le32(mtk->ippc + IPPC_U2_CTRL(i), CTRL_U2_PORT_PDN); + + /* power down host ip */ + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); + + return 0; +} + +static int xhci_mtk_ssusb_init(struct mtk_xhci *mtk) +{ + u32 value; + + /* reset whole ip */ + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); + udelay(1); + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); + + value = readl(mtk->ippc + IPPC_IP_XHCI_CAP); + mtk->num_u3ports = CAP_U3_PORT_NUM(value); + mtk->num_u2ports = CAP_U2_PORT_NUM(value); + dev_info(mtk->dev, "u2p:%d, u3p:%d\n", + mtk->num_u2ports, mtk->num_u3ports); + + return xhci_mtk_host_enable(mtk); +} + +static int xhci_mtk_ofdata_get(struct mtk_xhci *mtk) +{ + struct udevice *dev = mtk->dev; + int ret = 0; + + mtk->hcd = devfdt_remap_addr_name(dev, "mac"); + if (!mtk->hcd) { + dev_err(dev, "failed to get xHCI base address\n"); + return -ENXIO; + } + + mtk->ippc = devfdt_remap_addr_name(dev, "ippc"); + if (!mtk->ippc) { + dev_err(dev, "failed to get IPPC base address\n"); + return -ENXIO; + } + + dev_info(dev, "hcd: 0x%p, ippc: 0x%p\n", mtk->hcd, mtk->ippc); + + ret = clk_get_bulk(dev, &mtk->clks); + if (ret) { + dev_err(dev, "failed to get clocks %d!\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vusb33-supply", + &mtk->vusb33_supply); + if (ret) + debug("can't get vusb33 regulator %d!\n", ret); + + ret = device_get_supply_regulator(dev, "vbus-supply", + &mtk->vbus_supply); + if (ret) + debug("can't get vbus regulator %d!\n", ret); + + return 0; +} + +static int xhci_mtk_ldos_enable(struct mtk_xhci *mtk) +{ + int ret; + + ret = regulator_set_enable(mtk->vusb33_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(mtk->dev, "failed to enable vusb33 %d!\n", ret); + return ret; + } + + ret = regulator_set_enable(mtk->vbus_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(mtk->dev, "failed to enable vbus %d!\n", ret); + regulator_set_enable(mtk->vusb33_supply, false); + return ret; + } + + return 0; +} + +static void xhci_mtk_ldos_disable(struct mtk_xhci *mtk) +{ + regulator_set_enable(mtk->vbus_supply, false); + regulator_set_enable(mtk->vusb33_supply, false); +} + +static int xhci_mtk_phy_setup(struct mtk_xhci *mtk) +{ + struct udevice *dev = mtk->dev; + struct phy_bulk *phys = &mtk->phys; + int ret; + + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret; + + ret = generic_phy_init_bulk(phys); + if (ret) + return ret; + + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys); + + return ret; +} + +static void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk) +{ + generic_phy_power_off_bulk(&mtk->phys); + generic_phy_exit_bulk(&mtk->phys); +} + +static int xhci_mtk_probe(struct udevice *dev) +{ + struct mtk_xhci *mtk = dev_get_priv(dev); + struct xhci_hcor *hcor; + int ret; + + mtk->dev = dev; + ret = xhci_mtk_ofdata_get(mtk); + if (ret) + return ret; + + ret = xhci_mtk_ldos_enable(mtk); + if (ret) + goto ldos_err; + + ret = clk_enable_bulk(&mtk->clks); + if (ret) + goto clks_err; + + ret = xhci_mtk_phy_setup(mtk); + if (ret) + goto phys_err; + + ret = xhci_mtk_ssusb_init(mtk); + if (ret) + goto ssusb_init_err; + + hcor = (struct xhci_hcor *)((uintptr_t)mtk->hcd + + HC_LENGTH(xhci_readl(&mtk->hcd->cr_capbase))); + + return xhci_register(dev, mtk->hcd, hcor); + +ssusb_init_err: + xhci_mtk_phy_shutdown(mtk); +phys_err: + clk_disable_bulk(&mtk->clks); +clks_err: + xhci_mtk_ldos_disable(mtk); +ldos_err: + return ret; +} + +static int xhci_mtk_remove(struct udevice *dev) +{ + struct mtk_xhci *mtk = dev_get_priv(dev); + + xhci_deregister(dev); + xhci_mtk_host_disable(mtk); + xhci_mtk_ldos_disable(mtk); + clk_disable_bulk(&mtk->clks); + + return 0; +} + +static const struct udevice_id xhci_mtk_ids[] = { + { .compatible = "mediatek,mtk-xhci" }, + { } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci-mtk", + .id = UCLASS_USB, + .of_match = xhci_mtk_ids, + .probe = xhci_mtk_probe, + .remove = xhci_mtk_remove, + .ops = &xhci_usb_ops, + .bind = dm_scan_fdt_dev, + .priv_auto_alloc_size = sizeof(struct mtk_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 40dee2e6d9..c370eb6394 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -610,6 +610,16 @@ static int xhci_set_configuration(struct usb_device *udev) ep_ctx[ep_index]->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | EP_AVG_TRB_LENGTH(avg_trb_len)); + + /* + * The MediaTek xHCI defines some extra SW parameters which + * are put into reserved DWs in Slot and Endpoint Contexts + * for synchronous endpoints. + */ + if (IS_ENABLED(CONFIG_USB_XHCI_MTK)) { + ep_ctx[ep_index]->reserved[0] = + cpu_to_le32(EP_BPKTS(1) | EP_BBM(1)); + } }
return xhci_configure_endpoints(udev, false); diff --git a/include/usb/xhci.h b/include/usb/xhci.h index 6017504488..20e4a21066 100644 --- a/include/usb/xhci.h +++ b/include/usb/xhci.h @@ -670,6 +670,9 @@ struct xhci_ep_ctx { /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0)
+/* reserved[0] bitmasks, MediaTek xHCI used */ +#define EP_BPKTS(p) (((p) & 0x7f) << 0) +#define EP_BBM(p) (((p) & 0x1) << 11)
/** * struct xhci_input_control_context

From: Chunfeng Yun chunfeng.yun@mediatek.com
Add usb, phy and clock nodes
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v3~v5: no changes
v2: 1. remove fixed clock clk20m --- arch/arm/dts/mt7629-rfb.dts | 8 ++++++++ arch/arm/dts/mt7629.dtsi | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+)
diff --git a/arch/arm/dts/mt7629-rfb.dts b/arch/arm/dts/mt7629-rfb.dts index 687fe1c029..bf84f76344 100644 --- a/arch/arm/dts/mt7629-rfb.dts +++ b/arch/arm/dts/mt7629-rfb.dts @@ -82,6 +82,14 @@ status = "okay"; };
+&xhci { + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + &watchdog { pinctrl-names = "default"; pinctrl-0 = <&watchdog_pins>; diff --git a/arch/arm/dts/mt7629.dtsi b/arch/arm/dts/mt7629.dtsi index 644d2da4a8..6850e0058d 100644 --- a/arch/arm/dts/mt7629.dtsi +++ b/arch/arm/dts/mt7629.dtsi @@ -11,6 +11,7 @@ #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/power/mt7629-power.h> #include <dt-bindings/reset/mt7629-reset.h> +#include <dt-bindings/phy/phy.h> #include "skeleton.dtsi"
/ { @@ -222,6 +223,46 @@ #size-cells = <0>; };
+ ssusbsys: ssusbsys@1a000000 { + compatible = "mediatek,mt7629-ssusbsys", "syscon"; + reg = <0x1a000000 0x1000>; + #clock-cells = <1>; + }; + + xhci: usb@1a0c0000 { + compatible = "mediatek,mt7629-xhci", "mediatek,mtk-xhci"; + reg = <0x1a0c0000 0x1000>, <0x1a0c3e00 0x0100>; + reg-names = "mac", "ippc"; + power-domains = <&scpsys MT7629_POWER_DOMAIN_HIF1>; + clocks = <&ssusbsys CLK_SSUSB_SYS_EN>, + <&ssusbsys CLK_SSUSB_REF_EN>, + <&ssusbsys CLK_SSUSB_MCU_EN>, + <&ssusbsys CLK_SSUSB_DMA_EN>; + clock-names = "sys_ck", "ref_ck", "mcu_ck", "dma_ck"; + phys = <&u2port0 PHY_TYPE_USB2>, <&u3port0 PHY_TYPE_USB3>; + status = "disabled"; + }; + + u3phy: usb-phy@1a0c4000 { + compatible = "mediatek,mt7629-tphy", "mediatek,generic-tphy-v2"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x1a0c4000 0x1000>; + status = "disabled"; + + u2port0: usb-phy@0 { + reg = <0x0 0x0700>; + #phy-cells = <1>; + clocks = <&ssusbsys CLK_SSUSB_U2_PHY_EN>; + clock-names = "ref"; + }; + + u3port0: usb-phy@700 { + reg = <0x0700 0x0700>; + #phy-cells = <1>; + }; + }; + ethsys: syscon@1b000000 { compatible = "mediatek,mt7629-ethsys", "syscon"; reg = <0x1b000000 0x1000>;

From: Chunfeng Yun chunfeng.yun@mediatek.com
1. add the address mapping related properties; 2. make "ref" clock optional, and add optional clock "da_ref"; 3. add the banks layout of TPHY V1 and V2;
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v2~v5: no changes --- doc/device-tree-bindings/phy/phy-mtk-tphy.txt | 78 ++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-)
diff --git a/doc/device-tree-bindings/phy/phy-mtk-tphy.txt b/doc/device-tree-bindings/phy/phy-mtk-tphy.txt index 037c5a4be5..8cd23d8c0b 100644 --- a/doc/device-tree-bindings/phy/phy-mtk-tphy.txt +++ b/doc/device-tree-bindings/phy/phy-mtk-tphy.txt @@ -7,10 +7,17 @@ controllers on MediaTek SoCs, such as, USB2.0, USB3.0, PCIe, and SATA. Required properties (controller (parent) node): - compatible : should be one of "mediatek,generic-tphy-v1" - - clocks : (deprecated, use port's clocks instead) a list of phandle + - clock-specifier pairs, one for each entry in clock-names - - clock-names : (deprecated, use port's one instead) must contain - "u3phya_ref": for reference clock of usb3.0 analog phy. + "mediatek,generic-tphy-v2" + +- #address-cells: the number of cells used to represent physical + base addresses. +- #size-cells: the number of cells used to represent the size of an address. +- ranges: the address mapping relationship to the parent, defined with + - empty value: if optional 'reg' is used. + - non-empty value: if optional 'reg' is not used. should set + the child's base address to 0, the physical address + within parent's address space, and the length of + the address map.
Required nodes : a sub-node is required for each port the controller provides. Address range information including the usual @@ -27,12 +34,6 @@ Optional properties (controller (parent) node):
Required properties (port (child) node): - reg : address and length of the register set for the port. -- clocks : a list of phandle + clock-specifier pairs, one for each - entry in clock-names -- clock-names : must contain - "ref": 48M reference clock for HighSpeed analog phy; and 26M - reference clock for SuperSpeed analog phy, sometimes is - 24M, 25M or 27M, depended on platform. - #phy-cells : should be 1 (See second example) cell after port phandle is phy type from: - PHY_TYPE_USB2 @@ -40,6 +41,17 @@ Required properties (port (child) node): - PHY_TYPE_PCIE - PHY_TYPE_SATA
+Optional properties (port (child) node): +- clocks : a list of phandle + clock-specifier pairs, one for each + entry in clock-names +- clock-names : may contain + "ref": 48M reference clock for HighSpeed (digital) phy; and 26M + reference clock for SuperSpeed (digital) phy, sometimes is + 24M, 25M or 27M, depended on platform. + "da_ref": the reference clock of analog phy, used if the clocks + of analog and digital phys are separated, otherwise uses + "ref" clock only if needed. + Example:
u3phy2: usb-phy@1a244000 { @@ -84,3 +96,49 @@ usb30: usb@11270000 { phy-names = "usb2-0", "usb3-0"; ... }; + +Layout differences of banks between TPHY V1 and V2 +------------------------------------------------------------- +IP V1: +port offset bank +shared 0x0000 SPLLC + 0x0100 FMREG +u2 port0 0x0800 U2PHY_COM +u3 port0 0x0900 U3PHYD + 0x0a00 U3PHYD_BANK2 + 0x0b00 U3PHYA + 0x0c00 U3PHYA_DA +u2 port1 0x1000 U2PHY_COM +u3 port1 0x1100 U3PHYD + 0x1200 U3PHYD_BANK2 + 0x1300 U3PHYA + 0x1400 U3PHYA_DA +u2 port2 0x1800 U2PHY_COM + ... + +IP V2: +port offset bank +u2 port0 0x0000 MISC + 0x0100 FMREG + 0x0300 U2PHY_COM +u3 port0 0x0700 SPLLC + 0x0800 CHIP + 0x0900 U3PHYD + 0x0a00 U3PHYD_BANK2 + 0x0b00 U3PHYA + 0x0c00 U3PHYA_DA +u2 port1 0x1000 MISC + 0x1100 FMREG + 0x1300 U2PHY_COM +u3 port1 0x1700 SPLLC + 0x1800 CHIP + 0x1900 U3PHYD + 0x1a00 U3PHYD_BANK2 + 0x1b00 U3PHYA + 0x1c00 U3PHYA_DA +u2 port2 0x2000 MISC + ... + + SPLLC shared by u3 ports and FMREG shared by u2 ports on +TPHY V1 are put back into each port; a new bank MISC for +u2 ports and CHIP for u3 ports are added on TPHY V2.

From: Chunfeng Yun chunfeng.yun@mediatek.com
Add dt-binding for MediaTek xHCI host controller
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v2~v5: no changes --- .../usb/mediatek,mtk-xhci.txt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt
diff --git a/doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt b/doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt new file mode 100644 index 0000000000..0447468a2d --- /dev/null +++ b/doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt @@ -0,0 +1,40 @@ +MediaTek xHCI + +The device node for USB3 host controller on MediaTek SoCs. + +Required properties: + - compatible : should be "mediatek,mtk-xhci" + - reg : specifies physical base address and size of the registers + - reg-names: should be "mac" for xHCI MAC and "ippc" for IP port control + - power-domains : a phandle to USB power domain node to control USB's + MTCMOS + - vusb33-supply : regulator of USB avdd3.3v + + - clocks : a list of phandle + clock-specifier pairs, one for each + entry in clock-names + - clock-names : must contain + "sys_ck": controller clock used by normal mode, + the following ones are optional: + "ref_ck": reference clock used by low power mode etc, + "mcu_ck": mcu_bus clock for register access, + "dma_ck": dma_bus clock for data transfer by DMA, + "xhci_ck": controller clock + + - phys : list of all the USB PHYs on this HCD + - phy-names: name specifier for the USB PHY + +Optional properties: + - vbus-supply : reference to the VBUS regulator; + +Example: +xhci: usb@1a0c0000 { + compatible = "mediatek,mt7629-xhci", "mediatek,mtk-xhci"; + reg = <0x1a0c0000 0x1000>, <0x1a0c3e00 0x0100>; + reg-names = "mac", "ippc"; + power-domains = <&scpsys MT7629_POWER_DOMAIN_HIF1>; + clocks = <&ssusbsys CLK_SSUSB_SYS_EN>, <&ssusbsys CLK_SSUSB_REF_EN>, + <&ssusbsys CLK_SSUSB_MCU_EN>, <&ssusbsys CLK_SSUSB_DMA_EN>; + clock-names = "sys_ck", "ref_ck", "mcu_ck", "dma_ck"; + phys = <&u2port0 PHY_TYPE_USB2>, <&u3port0 PHY_TYPE_USB3>; + status = "disabled"; +};

From: Chunfeng Yun chunfeng.yun@mediatek.com
Add dt-binding files of xhci-mtk and phys, C files of phys.
Signed-off-by: Chunfeng Yun chunfeng.yun@mediatek.com Signed-off-by: Frank Wunderlich frank-w@public-files.de Reviewed-by: Weijie Gao weijie.gao@mediatek.com --- v8: no changes
v7: no changes
v6: add Reviewed-by Weijie
v5: new patch --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index dd92af5182..317e0534d7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -244,9 +244,12 @@ S: Maintained F: arch/arm/mach-mediatek/ F: arch/arm/include/asm/arch-mediatek/ F: board/mediatek/ +F: doc/device-tree-bindings/phy/phy-mtk-* +F: doc/device-tree-bindings/usb/mediatek,* F: doc/README.mediatek F: drivers/clk/mediatek/ F: drivers/mmc/mtk-sd.c +F: drivers/phy/phy-mtk-* F: drivers/pinctrl/mediatek/ F: drivers/power/domain/mtk-power-domain.c F: drivers/ram/mediatek/

On 5/2/20 11:35 AM, Frank Wunderlich wrote:
The series of patches are used to support xHCI host controller on MediaTek SoCs which has a glue layer IPPC (IP Port Control), and add USB function on T-PHY including T-PHY V1 and V2; Finally add USB related nodes for MT7629 platform.
The files of xhci-mtk.* and phy-mtk-tphy.* are ported from Linux Kenrel 5.6-rc1
v8 changes: 1. fix build-error reported by Marek in Part 5 2. make 2 functions static in part 10 suggested by Jagan 3. added review by Jagan on parts 4 + 10
Applied, thanks.
btw do you really need such a massive CC list ?

Am 2. Mai 2020 12:32:56 MESZ schrieb Marek Vasut marex@denx.de:
Applied, thanks.
btw do you really need such a massive CC list ?
Thanks,
i took To/Cc from v7 (i guess coverletter) to prepare my coverletter and used it for complete series. regards Frank

On 5/3/20 8:33 AM, Frank Wunderlich wrote:
Am 2. Mai 2020 12:32:56 MESZ schrieb Marek Vasut marex@denx.de:
Applied, thanks.
btw do you really need such a massive CC list ?
Thanks,
i took To/Cc from v7 (i guess coverletter) to prepare my coverletter and used it for complete series.
But was such a massive CC really necessary ?

On Sun, 2020-05-03 at 13:43 +0200, Marek Vasut wrote:
On 5/3/20 8:33 AM, Frank Wunderlich wrote:
Am 2. Mai 2020 12:32:56 MESZ schrieb Marek Vasut marex@denx.de:
Applied, thanks.
btw do you really need such a massive CC list ?
Thanks,
i took To/Cc from v7 (i guess coverletter) to prepare my coverletter and used it for complete series.
But was such a massive CC really necessary ?
I got them by get_maintainer.pl

Am 2. Mai 2020 12:32:56 MESZ schrieb Marek Vasut marex@denx.de:
Applied, thanks.
Hi Tom,
Can you merge it too?
Regards Frank regards Frank

On Mon, May 04, 2020 at 06:52:17PM +0200, Frank Wunderlich wrote:
Am 2. Mai 2020 12:32:56 MESZ schrieb Marek Vasut marex@denx.de:
Applied, thanks.
Hi Tom,
Can you merge it too?
When Marek sends me a PR with it, yes.
participants (5)
-
Chunfeng Yun
-
Frank Wunderlich
-
Frank Wunderlich
-
Marek Vasut
-
Tom Rini