mirror of
https://github.com/openwrt/openwrt.git
synced 2026-06-17 14:50:15 +04:00
econet: en7528: add USB support
Add USB host support for EN7528 SoC: - New phy-en7528-usb driver with U2 slew rate calibration and U3 RX impedance tuning, based on GPL vendor code and the Airoha AN7581 USB PHY driver - xHCI LTSSM timing quirk for TD 6.5 compliance (patch 915) - USB PHY and xHCI DTS nodes with IPPC register mapping - VBUS power via regulator-fixed for DASAN H660GM-A - Enable USB, xHCI, PHY, and regulator kernel configs Signed-off-by: Ahmed Naseef <naseefkm@gmail.com> Link: https://github.com/openwrt/openwrt/pull/22498 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
committed by
Hauke Mehrtens
parent
4e26edb2e3
commit
62c1c6a946
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <dt-bindings/interrupt-controller/mips-gic.h>
|
||||
#include <dt-bindings/clock/en7523-clk.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <dt-bindings/reset/airoha,en7523-reset.h>
|
||||
|
||||
/ {
|
||||
@@ -131,6 +132,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
usb_phy: usb-phy@1fa80000 {
|
||||
compatible = "econet,en7528-usb-phy";
|
||||
reg = <0x1fa80000 0x1400>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
usb: usb@1fb90000 {
|
||||
compatible = "econet,en7528-xhci";
|
||||
reg = <0x1fb90000 0x3e00>,
|
||||
<0x1fb93e00 0x100>;
|
||||
reg-names = "mac", "ippc";
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <GIC_SHARED 17 IRQ_TYPE_LEVEL_HIGH>;
|
||||
phys = <&usb_phy PHY_TYPE_USB2>, <&usb_phy PHY_TYPE_USB3>;
|
||||
usb3-lpm-capable;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
uart: serial@1fbf0000 {
|
||||
compatible = "airoha,en7523-uart";
|
||||
reg = <0x1fbf0000 0x30>;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_USB;
|
||||
gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
|
||||
linux,default-trigger = "usbport";
|
||||
trigger-sources = <&usb>;
|
||||
};
|
||||
|
||||
led-lan1-green {
|
||||
|
||||
@@ -77,14 +77,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
gpio_export {
|
||||
compatible = "gpio-export";
|
||||
|
||||
usb-power {
|
||||
gpio-export,name = "usb-power";
|
||||
gpio-export,output = <1>;
|
||||
gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
reg_usb_vbus: regulator-usb-vbus {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "usb-vbus";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
|
||||
enable-active-high;
|
||||
};
|
||||
|
||||
keys {
|
||||
@@ -105,6 +104,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
&usb {
|
||||
status = "okay";
|
||||
vbus-supply = <®_usb_vbus>;
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -58,6 +58,7 @@ CONFIG_FS_IOMAP=y
|
||||
CONFIG_FUNCTION_ALIGNMENT=0
|
||||
CONFIG_FW_LOADER_PAGED_BUF=y
|
||||
CONFIG_FW_LOADER_SYSFS=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_ATOMIC64=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CMOS_UPDATE=y
|
||||
@@ -140,6 +141,7 @@ CONFIG_NET_EGRESS=y
|
||||
CONFIG_NET_FLOW_LIMIT=y
|
||||
CONFIG_NET_INGRESS=y
|
||||
CONFIG_NET_XGRESS=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NR_CPUS=4
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_NVMEM_LAYOUTS=y
|
||||
@@ -165,6 +167,7 @@ CONFIG_PCI_MSI_ARCH_FALLBACKS=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PGTABLE_LEVELS=2
|
||||
CONFIG_PHY_EN7528_PCIE=y
|
||||
CONFIG_PHY_EN7528_USB=y
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_QUEUED_RWLOCKS=y
|
||||
CONFIG_QUEUED_SPINLOCKS=y
|
||||
@@ -172,6 +175,8 @@ CONFIG_RANDSTRUCT_NONE=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_REGMAP_MMIO=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_RESET_CONTROLLER=y
|
||||
CONFIG_RFS_ACCEL=y
|
||||
CONFIG_RPS=y
|
||||
@@ -217,6 +222,12 @@ CONFIG_TIMER_PROBE=y
|
||||
CONFIG_TREE_RCU=y
|
||||
CONFIG_TREE_SRCU=y
|
||||
CONFIG_UBIFS_FS=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_COMMON=y
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_XHCI_MTK=y
|
||||
# CONFIG_USB_XHCI_PLATFORM is not set
|
||||
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
|
||||
CONFIG_USE_OF=y
|
||||
CONFIG_WEAK_ORDERING=y
|
||||
|
||||
@@ -10,7 +10,7 @@ TARGET_DEVICES += en7528_generic
|
||||
define Device/dasan_h660gm-a
|
||||
DEVICE_VENDOR := DASAN
|
||||
DEVICE_MODEL := H660GM-A
|
||||
DEVICE_PACKAGES := kmod-mt7603 kmod-mt7615e kmod-mt7663-firmware-ap
|
||||
DEVICE_PACKAGES := kmod-usb2 kmod-mt7603 kmod-mt7615e kmod-mt7663-firmware-ap
|
||||
TRX_MODEL := Dewberry
|
||||
IMAGES := tclinux.trx
|
||||
IMAGE/tclinux.trx := append-kernel | lzma | tclinux-trx
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
From: Ahmed Naseef <naseefkm@gmail.com>
|
||||
Subject: phy: add EN7528 USB PHY driver
|
||||
|
||||
Add USB PHY driver for EcoNet EN7528 SoC.
|
||||
|
||||
Based on GPL vendor code at https://github.com/keenetic/kernel-49
|
||||
and the Airoha AN7581 USB PHY driver by Christian Marangi.
|
||||
|
||||
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
|
||||
--- a/drivers/phy/Kconfig
|
||||
+++ b/drivers/phy/Kconfig
|
||||
@@ -93,6 +93,17 @@ config PHY_EN7528_PCIE
|
||||
This driver provides PHY initialization for the two PCIe ports
|
||||
on EN7528 SoC.
|
||||
|
||||
+config PHY_EN7528_USB
|
||||
+ tristate "EcoNet EN7528 USB PHY Driver"
|
||||
+ depends on ECONET || COMPILE_TEST
|
||||
+ depends on OF
|
||||
+ select GENERIC_PHY
|
||||
+ select REGMAP_MMIO
|
||||
+ help
|
||||
+ Say 'Y' here to add support for EcoNet EN7528 USB PHY driver.
|
||||
+ This driver creates the basic PHY instance and provides
|
||||
+ initialization callback for the USB port.
|
||||
+
|
||||
source "drivers/phy/allwinner/Kconfig"
|
||||
source "drivers/phy/amlogic/Kconfig"
|
||||
source "drivers/phy/broadcom/Kconfig"
|
||||
--- a/drivers/phy/Makefile
|
||||
+++ b/drivers/phy/Makefile
|
||||
@@ -12,6 +12,7 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-
|
||||
obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
|
||||
obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o
|
||||
obj-$(CONFIG_PHY_EN7528_PCIE) += phy-en7528-pcie.o
|
||||
+obj-$(CONFIG_PHY_EN7528_USB) += phy-en7528-usb.o
|
||||
obj-y += allwinner/ \
|
||||
amlogic/ \
|
||||
broadcom/ \
|
||||
--- /dev/null
|
||||
+++ b/drivers/phy/phy-en7528-usb.c
|
||||
@@ -0,0 +1,316 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * EcoNet EN7528 USB PHY driver
|
||||
+ *
|
||||
+ * Based on GPL vendor code at https://github.com/keenetic/kernel-49
|
||||
+ * and the Airoha AN7581 USB PHY driver by
|
||||
+ * Christian Marangi <ansuelsmth@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <dt-bindings/phy/phy.h>
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/math.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/phy/phy.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+/* Frequency Meter registers (shared across ports) */
|
||||
+#define EN7528_USB_PHY_FMCR0 0x100
|
||||
+#define EN7528_USB_PHY_MONCLK_SEL GENMASK(27, 26)
|
||||
+#define EN7528_USB_PHY_FREQDET_EN BIT(24)
|
||||
+#define EN7528_USB_PHY_CYCLECNT GENMASK(23, 0)
|
||||
+#define EN7528_USB_PHY_FMMONR0 0x10c
|
||||
+#define EN7528_USB_PHY_FMMONR1 0x110
|
||||
+#define EN7528_USB_PHY_FRCK_EN BIT(8)
|
||||
+
|
||||
+/* U2 PHY port bases */
|
||||
+#define EN7528_USB_PHY_U2_P0_BASE 0x300
|
||||
+#define EN7528_USB_PHY_U2_P1_BASE 0x1300
|
||||
+#define EN7528_USB_PHY_NUM_U2_PORTS 2
|
||||
+
|
||||
+/* U2 PHY register offsets (relative to port base) */
|
||||
+#define EN7528_USB_PHY_ACR0 0x10
|
||||
+#define EN7528_USB_PHY_HSTX_SRCAL_EN BIT(23)
|
||||
+#define EN7528_USB_PHY_HSTX_SRCTRL GENMASK(18, 16)
|
||||
+#define EN7528_USB_PHY_ACR3 0x1c
|
||||
+#define EN7528_USB_PHY_ACR3_ENABLE 0xC0240000
|
||||
+
|
||||
+/* U3 PHYA registers (base at +0xb00) */
|
||||
+#define EN7528_USB_PHY_U3_PHYA_REG11 0xb2c
|
||||
+#define EN7528_USB_PHY_RX_IMPSEL GENMASK(13, 12)
|
||||
+
|
||||
+#define EN7528_USB_PHY_FM_DET_CYCLE_CNT 1024
|
||||
+#define EN7528_USB_PHY_REF_CK 20 /* MHz */
|
||||
+#define EN7528_USB_PHY_SR_COEF 28
|
||||
+#define EN7528_USB_PHY_SR_COEF_DIVISOR 1000
|
||||
+#define EN7528_USB_PHY_DEFAULT_SR 4
|
||||
+
|
||||
+#define EN7528_USB_PHY_FREQDET_SLEEP 1000 /* 1ms */
|
||||
+#define EN7528_USB_PHY_FREQDET_TIMEOUT (EN7528_USB_PHY_FREQDET_SLEEP * 10)
|
||||
+
|
||||
+static const unsigned int en7528_u2_port_bases[EN7528_USB_PHY_NUM_U2_PORTS] = {
|
||||
+ EN7528_USB_PHY_U2_P0_BASE,
|
||||
+ EN7528_USB_PHY_U2_P1_BASE,
|
||||
+};
|
||||
+
|
||||
+struct en7528_usb_phy_instance {
|
||||
+ struct phy *phy;
|
||||
+ u32 type;
|
||||
+};
|
||||
+
|
||||
+enum en7528_usb_phy_type {
|
||||
+ EN7528_PHY_USB2,
|
||||
+ EN7528_PHY_USB3,
|
||||
+
|
||||
+ EN7528_PHY_USB_MAX,
|
||||
+};
|
||||
+
|
||||
+struct en7528_usb_phy_priv {
|
||||
+ struct device *dev;
|
||||
+ struct regmap *regmap;
|
||||
+
|
||||
+ struct en7528_usb_phy_instance *phys[EN7528_PHY_USB_MAX];
|
||||
+};
|
||||
+
|
||||
+static void en7528_usb_phy_slew_rate_calibration(struct en7528_usb_phy_priv *priv,
|
||||
+ unsigned int port_id,
|
||||
+ unsigned int port_base)
|
||||
+{
|
||||
+ unsigned int acr0 = port_base + EN7528_USB_PHY_ACR0;
|
||||
+ u32 fm_out;
|
||||
+ u32 srctrl;
|
||||
+
|
||||
+ /* Enable HS TX SR calibration */
|
||||
+ regmap_set_bits(priv->regmap, acr0,
|
||||
+ EN7528_USB_PHY_HSTX_SRCAL_EN);
|
||||
+
|
||||
+ usleep_range(1000, 1500);
|
||||
+
|
||||
+ /* Enable free run clock */
|
||||
+ regmap_set_bits(priv->regmap, EN7528_USB_PHY_FMMONR1,
|
||||
+ EN7528_USB_PHY_FRCK_EN);
|
||||
+
|
||||
+ /* Select monitor clock: port 0 = MONCLK_SEL 0, port 1 = MONCLK_SEL 1 */
|
||||
+ regmap_update_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
|
||||
+ EN7528_USB_PHY_MONCLK_SEL,
|
||||
+ FIELD_PREP(EN7528_USB_PHY_MONCLK_SEL, port_id));
|
||||
+
|
||||
+ /* Set cycle count */
|
||||
+ regmap_update_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
|
||||
+ EN7528_USB_PHY_CYCLECNT,
|
||||
+ FIELD_PREP(EN7528_USB_PHY_CYCLECNT,
|
||||
+ EN7528_USB_PHY_FM_DET_CYCLE_CNT));
|
||||
+
|
||||
+ /* Enable frequency meter */
|
||||
+ regmap_set_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
|
||||
+ EN7528_USB_PHY_FREQDET_EN);
|
||||
+
|
||||
+ /* Timeout can happen and we will apply default value at the end */
|
||||
+ (void)regmap_read_poll_timeout(priv->regmap, EN7528_USB_PHY_FMMONR0,
|
||||
+ fm_out, fm_out,
|
||||
+ EN7528_USB_PHY_FREQDET_SLEEP,
|
||||
+ EN7528_USB_PHY_FREQDET_TIMEOUT);
|
||||
+
|
||||
+ /* Disable frequency meter */
|
||||
+ regmap_clear_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
|
||||
+ EN7528_USB_PHY_FREQDET_EN);
|
||||
+
|
||||
+ /* Disable free run clock */
|
||||
+ regmap_clear_bits(priv->regmap, EN7528_USB_PHY_FMMONR1,
|
||||
+ EN7528_USB_PHY_FRCK_EN);
|
||||
+
|
||||
+ /* Disable HS TX SR calibration */
|
||||
+ regmap_clear_bits(priv->regmap, acr0,
|
||||
+ EN7528_USB_PHY_HSTX_SRCAL_EN);
|
||||
+
|
||||
+ usleep_range(1000, 1500);
|
||||
+
|
||||
+ if (!fm_out) {
|
||||
+ srctrl = EN7528_USB_PHY_DEFAULT_SR;
|
||||
+ dev_err(priv->dev, "port%u: frequency not detected, using default SR calibration.\n",
|
||||
+ port_id);
|
||||
+ } else {
|
||||
+ /* (1024 / FM_OUT) * REF_CK * SR_COEF */
|
||||
+ srctrl = EN7528_USB_PHY_REF_CK * EN7528_USB_PHY_SR_COEF;
|
||||
+ srctrl = (srctrl * EN7528_USB_PHY_FM_DET_CYCLE_CNT) / fm_out;
|
||||
+ srctrl = DIV_ROUND_CLOSEST(srctrl, EN7528_USB_PHY_SR_COEF_DIVISOR);
|
||||
+ dev_dbg(priv->dev, "port%u: SR calibration applied: %x\n",
|
||||
+ port_id, srctrl);
|
||||
+ }
|
||||
+
|
||||
+ regmap_update_bits(priv->regmap, acr0,
|
||||
+ EN7528_USB_PHY_HSTX_SRCTRL,
|
||||
+ FIELD_PREP(EN7528_USB_PHY_HSTX_SRCTRL, srctrl));
|
||||
+}
|
||||
+
|
||||
+static int en7528_usb_phy_init(struct phy *phy)
|
||||
+{
|
||||
+ struct en7528_usb_phy_instance *instance = phy_get_drvdata(phy);
|
||||
+ struct en7528_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ switch (instance->type) {
|
||||
+ case PHY_TYPE_USB2:
|
||||
+ /* Enable both U2 PHY ports before calibration */
|
||||
+ for (i = 0; i < EN7528_USB_PHY_NUM_U2_PORTS; i++)
|
||||
+ regmap_write(priv->regmap,
|
||||
+ en7528_u2_port_bases[i] + EN7528_USB_PHY_ACR3,
|
||||
+ EN7528_USB_PHY_ACR3_ENABLE);
|
||||
+ break;
|
||||
+ case PHY_TYPE_USB3:
|
||||
+ /* Combo PHY Rx R mean value too high, tune -5 Ohm */
|
||||
+ regmap_update_bits(priv->regmap,
|
||||
+ EN7528_USB_PHY_U3_PHYA_REG11,
|
||||
+ EN7528_USB_PHY_RX_IMPSEL,
|
||||
+ FIELD_PREP(EN7528_USB_PHY_RX_IMPSEL, 0x1));
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int en7528_usb_phy_power_on(struct phy *phy)
|
||||
+{
|
||||
+ struct en7528_usb_phy_instance *instance = phy_get_drvdata(phy);
|
||||
+ struct en7528_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ if (instance->type != PHY_TYPE_USB2)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Calibrate slew rate for both U2 PHY ports */
|
||||
+ for (i = 0; i < EN7528_USB_PHY_NUM_U2_PORTS; i++)
|
||||
+ en7528_usb_phy_slew_rate_calibration(priv, i,
|
||||
+ en7528_u2_port_bases[i]);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct phy_ops en7528_usb_phy_ops = {
|
||||
+ .init = en7528_usb_phy_init,
|
||||
+ .power_on = en7528_usb_phy_power_on,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static struct phy *en7528_usb_phy_xlate(struct device *dev,
|
||||
+ const struct of_phandle_args *args)
|
||||
+{
|
||||
+ struct en7528_usb_phy_priv *priv = dev_get_drvdata(dev);
|
||||
+ struct en7528_usb_phy_instance *instance = NULL;
|
||||
+ unsigned int index, phy_type;
|
||||
+
|
||||
+ if (args->args_count != 1) {
|
||||
+ dev_err(dev, "invalid number of cells in 'phy' property\n");
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ phy_type = args->args[0];
|
||||
+ if (!(phy_type == PHY_TYPE_USB2 || phy_type == PHY_TYPE_USB3)) {
|
||||
+ dev_err(dev, "unsupported device type: %d\n", phy_type);
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ for (index = 0; index < EN7528_PHY_USB_MAX; index++)
|
||||
+ if (priv->phys[index] &&
|
||||
+ phy_type == priv->phys[index]->type) {
|
||||
+ instance = priv->phys[index];
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!instance) {
|
||||
+ dev_err(dev, "failed to find appropriate phy\n");
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ return instance->phy;
|
||||
+}
|
||||
+
|
||||
+static const struct regmap_config en7528_usb_phy_regmap_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .val_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .max_register = EN7528_USB_PHY_U2_P1_BASE + EN7528_USB_PHY_ACR3,
|
||||
+};
|
||||
+
|
||||
+static int en7528_usb_phy_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct phy_provider *phy_provider;
|
||||
+ struct en7528_usb_phy_priv *priv;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ unsigned int index;
|
||||
+ void *base;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ priv->dev = dev;
|
||||
+
|
||||
+ base = devm_platform_ioremap_resource(pdev, 0);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
+ &en7528_usb_phy_regmap_config);
|
||||
+ if (IS_ERR(priv->regmap))
|
||||
+ return PTR_ERR(priv->regmap);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, priv);
|
||||
+
|
||||
+ for (index = 0; index < EN7528_PHY_USB_MAX; index++) {
|
||||
+ struct en7528_usb_phy_instance *instance;
|
||||
+ enum en7528_usb_phy_type phy_type;
|
||||
+
|
||||
+ switch (index) {
|
||||
+ case EN7528_PHY_USB2:
|
||||
+ phy_type = PHY_TYPE_USB2;
|
||||
+ break;
|
||||
+ case EN7528_PHY_USB3:
|
||||
+ phy_type = PHY_TYPE_USB3;
|
||||
+ break;
|
||||
+ default:
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
|
||||
+ if (!instance)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ instance->type = phy_type;
|
||||
+ priv->phys[index] = instance;
|
||||
+
|
||||
+ instance->phy = devm_phy_create(dev, NULL, &en7528_usb_phy_ops);
|
||||
+ if (IS_ERR(instance->phy))
|
||||
+ return dev_err_probe(dev, PTR_ERR(instance->phy),
|
||||
+ "failed to create phy\n");
|
||||
+
|
||||
+ phy_set_drvdata(instance->phy, instance);
|
||||
+ }
|
||||
+
|
||||
+ phy_provider = devm_of_phy_provider_register(dev,
|
||||
+ en7528_usb_phy_xlate);
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(phy_provider);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id en7528_usb_phy_match[] = {
|
||||
+ { .compatible = "econet,en7528-usb-phy" },
|
||||
+ { },
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, en7528_usb_phy_match);
|
||||
+
|
||||
+static struct platform_driver en7528_usb_phy_driver = {
|
||||
+ .probe = en7528_usb_phy_probe,
|
||||
+ .driver = {
|
||||
+ .name = "en7528-usb-phy",
|
||||
+ .of_match_table = en7528_usb_phy_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(en7528_usb_phy_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_DESCRIPTION("EcoNet EN7528 USB PHY driver");
|
||||
@@ -0,0 +1,58 @@
|
||||
From: Ahmed Naseef <naseefkm@gmail.com>
|
||||
Subject: usb: xhci-mtk: add EN7528 LTSSM timing quirk
|
||||
|
||||
EN7528 needs LTSSM Timing Parameter 5 configured to fix
|
||||
TD 6.5 compliance test failures.
|
||||
|
||||
Based on GPL vendor code:
|
||||
https://github.com/keenetic/kernel-49/commit/ee0e0b7cf28c208cd5b892ea96180ffae0de9b7f
|
||||
|
||||
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
|
||||
--- a/drivers/usb/host/xhci-mtk.c
|
||||
+++ b/drivers/usb/host/xhci-mtk.c
|
||||
@@ -80,6 +80,8 @@
|
||||
#define SS_GEN2_EOF_CFG 0x990
|
||||
#define SSG2EOF_OFFSET 0x3c
|
||||
|
||||
+#define XHCI_MTK_LTSSM_TIMING_PARAMETER5 0x251c
|
||||
+
|
||||
#define XSEOF_OFFSET_MASK GENMASK(11, 0)
|
||||
|
||||
/* usb remote wakeup registers in syscon */
|
||||
@@ -191,6 +193,18 @@ static void xhci_mtk_rxfifo_depth_set(st
|
||||
writel(value, hcd->regs + HSCH_CFG1);
|
||||
}
|
||||
|
||||
+/* EN7528: fix TD 6.5 compliance test failure */
|
||||
+static void xhci_mtk_ltssm_quirk(struct xhci_hcd_mtk *mtk)
|
||||
+{
|
||||
+ struct device *dev = mtk->dev;
|
||||
+ struct usb_hcd *hcd = mtk->hcd;
|
||||
+
|
||||
+ if (!of_device_is_compatible(dev->of_node, "econet,en7528-xhci"))
|
||||
+ return;
|
||||
+
|
||||
+ writel(0x203e8, hcd->regs + XHCI_MTK_LTSSM_TIMING_PARAMETER5);
|
||||
+}
|
||||
+
|
||||
static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk)
|
||||
{
|
||||
/* workaround only for mt8195 */
|
||||
@@ -198,6 +212,9 @@ static void xhci_mtk_init_quirk(struct x
|
||||
|
||||
/* workaround for SoCs using SSUSB about before IPM v1.6.0 */
|
||||
xhci_mtk_rxfifo_depth_set(mtk);
|
||||
+
|
||||
+ /* EN7528 LTSSM timing fix */
|
||||
+ xhci_mtk_ltssm_quirk(mtk);
|
||||
}
|
||||
|
||||
static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
|
||||
@@ -846,6 +863,7 @@ static const struct dev_pm_ops xhci_mtk_
|
||||
static const struct of_device_id mtk_xhci_of_match[] = {
|
||||
{ .compatible = "mediatek,mt8173-xhci"},
|
||||
{ .compatible = "mediatek,mt8195-xhci"},
|
||||
+ { .compatible = "econet,en7528-xhci"},
|
||||
{ .compatible = "mediatek,mtk-xhci"},
|
||||
{ },
|
||||
};
|
||||
Reference in New Issue
Block a user