Merge git://git.denx.de/u-boot-riscv
authorTom Rini <trini@konsulko.com>
Thu, 9 May 2019 11:11:52 +0000 (07:11 -0400)
committerTom Rini <trini@konsulko.com>
Thu, 9 May 2019 11:11:52 +0000 (07:11 -0400)
- Correct SYS_TEXT_BASE for qemu.
- Support booti.
- Increase SYSBOOTM_LEN for Fedora/RISCV kernel.
- Support SMP booting from flash.

27 files changed:
arch/arm/dts/sama5d3xcm.dtsi
arch/arm/dts/sama5d3xcm_cmp.dtsi
arch/arm/dts/socfpga_arria5_socdk.dts
arch/arm/dts/socfpga_cyclone5_is1.dts
arch/arm/dts/socfpga_cyclone5_socdk.dts
arch/arm/dts/socfpga_cyclone5_sockit.dts
arch/arm/dts/socfpga_cyclone5_vining_fpga.dts
arch/arm/include/asm/arch-meson/usb.h [new file with mode: 0644]
arch/arm/mach-meson/board-g12a.c
board/ti/ks2_evm/mux-k2g.h
cmd/mdio.c
doc/device-tree-bindings/net/micrel-ksz90x1.txt
drivers/net/ldpaa_eth/ldpaa_eth.c
drivers/net/phy/Kconfig
drivers/net/phy/aquantia.c
drivers/net/phy/micrel_ksz90x1.c
drivers/net/phy/phy.c
drivers/net/phy/realtek.c
drivers/net/phy/ti.c
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/meson-g12a-usb2.c [new file with mode: 0644]
drivers/phy/meson-g12a-usb3-pcie.c [new file with mode: 0644]
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/dwc3-meson-g12a.c [new file with mode: 0644]
include/phy.h

index 2cf9c3611db60a5f6566be6898adab72e8a629d2..d123057f304f8d9f845d6f5f81946907648fb394 100644 (file)
                                        reg = <0x1>;
                                        interrupt-parent = <&pioB>;
                                        interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
-                                       txen-skew-ps = <800>;
-                                       txc-skew-ps = <3000>;
-                                       rxdv-skew-ps = <400>;
-                                       rxc-skew-ps = <3000>;
-                                       rxd0-skew-ps = <400>;
-                                       rxd1-skew-ps = <400>;
-                                       rxd2-skew-ps = <400>;
-                                       rxd3-skew-ps = <400>;
+                                       txen-skew-ps = <480>;
+                                       txc-skew-ps = <1800>;
+                                       rxdv-skew-ps = <240>;
+                                       rxc-skew-ps = <1800>;
+                                       rxd0-skew-ps = <240>;
+                                       rxd1-skew-ps = <240>;
+                                       rxd2-skew-ps = <240>;
+                                       rxd3-skew-ps = <240>;
                                };
 
                                ethernet-phy@7 {
                                        reg = <0x7>;
                                        interrupt-parent = <&pioB>;
                                        interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
-                                       txen-skew-ps = <800>;
-                                       txc-skew-ps = <3000>;
-                                       rxdv-skew-ps = <400>;
-                                       rxc-skew-ps = <3000>;
-                                       rxd0-skew-ps = <400>;
-                                       rxd1-skew-ps = <400>;
-                                       rxd2-skew-ps = <400>;
-                                       rxd3-skew-ps = <400>;
+                                       txen-skew-ps = <480>;
+                                       txc-skew-ps = <1800>;
+                                       rxdv-skew-ps = <240>;
+                                       rxc-skew-ps = <1800>;
+                                       rxd0-skew-ps = <240>;
+                                       rxd1-skew-ps = <240>;
+                                       rxd2-skew-ps = <240>;
+                                       rxd3-skew-ps = <240>;
                                };
                        };
                };
index 77638c3cbeaa63f3134cbdefbfa20495aa617c7f..332b057e0a9c525658ece7cb4b54589b184532ca 100644 (file)
                                        reg = <0x1>;
                                        interrupt-parent = <&pioB>;
                                        interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
-                                       txen-skew-ps = <800>;
-                                       txc-skew-ps = <3000>;
-                                       rxdv-skew-ps = <400>;
-                                       rxc-skew-ps = <3000>;
-                                       rxd0-skew-ps = <400>;
-                                       rxd1-skew-ps = <400>;
-                                       rxd2-skew-ps = <400>;
-                                       rxd3-skew-ps = <400>;
+                                       txen-skew-ps = <480>;
+                                       txc-skew-ps = <1800>;
+                                       rxdv-skew-ps = <240>;
+                                       rxc-skew-ps = <1800>;
+                                       rxd0-skew-ps = <240>;
+                                       rxd1-skew-ps = <240>;
+                                       rxd2-skew-ps = <240>;
+                                       rxd3-skew-ps = <240>;
                                };
 
                                ethernet-phy@7 {
                                        reg = <0x7>;
                                        interrupt-parent = <&pioB>;
                                        interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
-                                       txen-skew-ps = <800>;
-                                       txc-skew-ps = <3000>;
-                                       rxdv-skew-ps = <400>;
-                                       rxc-skew-ps = <3000>;
-                                       rxd0-skew-ps = <400>;
-                                       rxd1-skew-ps = <400>;
-                                       rxd2-skew-ps = <400>;
-                                       rxd3-skew-ps = <400>;
+                                       txen-skew-ps = <480>;
+                                       txc-skew-ps = <1800>;
+                                       rxdv-skew-ps = <240>;
+                                       rxc-skew-ps = <1800>;
+                                       rxd0-skew-ps = <240>;
+                                       rxd1-skew-ps = <240>;
+                                       rxd2-skew-ps = <240>;
+                                       rxd3-skew-ps = <240>;
                                };
                        };
 
index 90e676e7019f23377dd05bbf70b45f7af0d47ea5..fa972e287f8d515f50fcf21d4c3cdbd0ba6a73f9 100644 (file)
@@ -67,9 +67,9 @@
        rxd2-skew-ps = <0>;
        rxd3-skew-ps = <0>;
        txen-skew-ps = <0>;
-       txc-skew-ps = <2600>;
+       txc-skew-ps = <1560>;
        rxdv-skew-ps = <0>;
-       rxc-skew-ps = <2000>;
+       rxc-skew-ps = <1200>;
 };
 
 &gpio0 {
index 2d314129230cf5e9082ae12eb5891c082c4a8406..a769498791ab5841e17f0434c816eae85d8f21a3 100644 (file)
@@ -43,9 +43,9 @@
        rxd2-skew-ps = <0>;
        rxd3-skew-ps = <0>;
        txen-skew-ps = <0>;
-       txc-skew-ps = <2600>;
+       txc-skew-ps = <1560>;
        rxdv-skew-ps = <0>;
-       rxc-skew-ps = <2000>;
+       rxc-skew-ps = <1200>;
 };
 
 &gpio1 {
index 6f138b2b26163ada37d0c1cb0462ed70d44a4b4e..95c7619b8d65f5b101aa92d707294d13bb4c5e12 100644 (file)
@@ -71,9 +71,9 @@
        rxd2-skew-ps = <0>;
        rxd3-skew-ps = <0>;
        txen-skew-ps = <0>;
-       txc-skew-ps = <2600>;
+       txc-skew-ps = <1560>;
        rxdv-skew-ps = <0>;
-       rxc-skew-ps = <2000>;
+       rxc-skew-ps = <1200>;
 };
 
 &gpio0 {
index c155ff02eb6e035a7c7e526635a80a5feda9b4d8..90669cde45e94f8e79906d3021c02b416d747887 100644 (file)
        rxd2-skew-ps = <0>;
        rxd3-skew-ps = <0>;
        txen-skew-ps = <0>;
-       txc-skew-ps = <2600>;
+       txc-skew-ps = <1560>;
        rxdv-skew-ps = <0>;
-       rxc-skew-ps = <2000>;
+       rxc-skew-ps = <1200>;
 };
 
 &gpio0 {       /* GPIO 0..29 */
index 355b3dbf438d09be7d781d28ebc1e9ed81de06a3..ac57f41cb570ba22e023ae54779be60355f67136 100644 (file)
@@ -85,9 +85,9 @@
                        rxd2-skew-ps = <0>;
                        rxd3-skew-ps = <0>;
                        txen-skew-ps = <0>;
-                       txc-skew-ps = <2600>;
+                       txc-skew-ps = <1560>;
                        rxdv-skew-ps = <0>;
-                       rxc-skew-ps = <2000>;
+                       rxc-skew-ps = <1200>;
                };
        };
 };
diff --git a/arch/arm/include/asm/arch-meson/usb.h b/arch/arm/include/asm/arch-meson/usb.h
new file mode 100644 (file)
index 0000000..b794b5c
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#ifndef __MESON_USB_H__
+#define __MESON_USB_H__
+
+int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode);
+
+#endif /* __MESON_USB_H__ */
index fc3764b9604f3b7eca04b096c2c6fb9d9535af8f..1652970fbde96f58235b7e6ead1a564c5f78287f 100644 (file)
 #include <asm/io.h>
 #include <asm/armv8/mmu.h>
 #include <linux/sizes.h>
+#include <usb.h>
+#include <linux/usb/otg.h>
+#include <asm/arch/usb.h>
+#include <usb/dwc2_udc.h>
 #include <phy.h>
+#include <clk.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -148,3 +153,124 @@ void meson_eth_init(phy_interface_t mode, unsigned int flags)
        /* Enable power gate */
        clrbits_le32(G12A_MEM_PD_REG_0, G12A_MEM_PD_REG_0_ETH_MASK);
 }
+
+#if CONFIG_IS_ENABLED(USB_DWC3_MESON_G12A) && \
+       CONFIG_IS_ENABLED(USB_GADGET_DWC2_OTG)
+static struct dwc2_plat_otg_data meson_g12a_dwc2_data;
+
+int board_usb_init(int index, enum usb_init_type init)
+{
+       struct fdtdec_phandle_args args;
+       const void *blob = gd->fdt_blob;
+       int node, dwc2_node;
+       struct udevice *dev, *clk_dev;
+       struct clk clk;
+       int ret;
+
+       /* find the usb glue node */
+       node = fdt_node_offset_by_compatible(blob, -1,
+                                            "amlogic,meson-g12a-usb-ctrl");
+       if (node < 0) {
+               debug("Not found usb-control node\n");
+               return -ENODEV;
+       }
+
+       if (!fdtdec_get_is_enabled(blob, node)) {
+               debug("usb is disabled in the device tree\n");
+               return -ENODEV;
+       }
+
+       ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
+       if (ret) {
+               debug("Not found usb-control device\n");
+               return ret;
+       }
+
+       /* find the dwc2 node */
+       dwc2_node = fdt_node_offset_by_compatible(blob, node,
+                                                 "amlogic,meson-g12a-usb");
+       if (dwc2_node < 0) {
+               debug("Not found dwc2 node\n");
+               return -ENODEV;
+       }
+
+       if (!fdtdec_get_is_enabled(blob, dwc2_node)) {
+               debug("dwc2 is disabled in the device tree\n");
+               return -ENODEV;
+       }
+
+       meson_g12a_dwc2_data.regs_otg = fdtdec_get_addr(blob, dwc2_node, "reg");
+       if (meson_g12a_dwc2_data.regs_otg == FDT_ADDR_T_NONE) {
+               debug("usbotg: can't get base address\n");
+               return -ENODATA;
+       }
+
+       /* Enable clock */
+       ret = fdtdec_parse_phandle_with_args(blob, dwc2_node, "clocks",
+                                            "#clock-cells", 0, 0, &args);
+       if (ret) {
+               debug("usbotg has no clocks defined in the device tree\n");
+               return ret;
+       }
+
+       ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &clk_dev);
+       if (ret)
+               return ret;
+
+       if (args.args_count != 1) {
+               debug("Can't find clock ID in the device tree\n");
+               return -ENODATA;
+       }
+
+       clk.dev = clk_dev;
+       clk.id = args.args[0];
+
+       ret = clk_enable(&clk);
+       if (ret) {
+               debug("Failed to enable usbotg clock\n");
+               return ret;
+       }
+
+       meson_g12a_dwc2_data.rx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+                                                    "g-rx-fifo-size", 0);
+       meson_g12a_dwc2_data.np_tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+                                                       "g-np-tx-fifo-size", 0);
+       meson_g12a_dwc2_data.tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+                                                    "g-tx-fifo-size", 0);
+
+       /* Switch to peripheral mode */
+       ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_PERIPHERAL);
+       if (ret)
+               return ret;
+
+       return dwc2_udc_probe(&meson_g12a_dwc2_data);
+}
+
+int board_usb_cleanup(int index, enum usb_init_type init)
+{
+       const void *blob = gd->fdt_blob;
+       struct udevice *dev;
+       int node;
+       int ret;
+
+       /* find the usb glue node */
+       node = fdt_node_offset_by_compatible(blob, -1,
+                                            "amlogic,meson-g12a-usb-ctrl");
+       if (node < 0)
+               return -ENODEV;
+
+       if (!fdtdec_get_is_enabled(blob, node))
+               return -ENODEV;
+
+       ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
+       if (ret)
+               return ret;
+
+       /* Switch to OTG mode */
+       ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_HOST);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+#endif
index 89c49f9e4fcde8ee42e309fe2ba37643bc4252a7..6aa785ea42502d200e61b5b6e1e23d6b997309c8 100644 (file)
@@ -126,22 +126,22 @@ struct pin_cfg k2g_evm_pin_cfg[] = {
        { 71,   MODE(0) },      /* MMC1POW TP124 */
 
                /* EMAC */
-       { 79,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD1 */
-       { 78,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD2 */
+       { 72,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXC */
        { 77,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD3 */
+       { 78,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD2 */
+       { 79,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD1 */
        { 80,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD0 */
-       { 94,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD0 */
-       { 93,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD1 */
-       { 92,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD2 */
-       { 91,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD3 */
+       { 81,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXCTL */
        { 85,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXC */
+       { 91,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD3 */
+       { 92,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD2 */
+       { 93,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD1 */
+       { 94,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD0 */
        { 95,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXCTL */
-       { 72,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXC */
-       { 81,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXCTL */
 
        /* MDIO */
-       { 99,   BUFFER_CLASS_B | PIN_PDIS | MODE(0) },  /* MDIO_CLK */
        { 98,   BUFFER_CLASS_B | PIN_PDIS | MODE(0) },  /* MDIO_DATA */
+       { 99,   BUFFER_CLASS_B | PIN_PDIS | MODE(0) },  /* MDIO_CLK */
 
        /* PWM */
        { 73,   MODE(4) },      /* SOC_EHRPWM3A */
@@ -350,22 +350,22 @@ struct pin_cfg k2g_ice_evm_pin_cfg[] = {
        { 135,  MODE(0) },      /* SOC_QSPI_CSN0 */
 
        /* EMAC */
-       { 79,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD1 */
-       { 78,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD2 */
+       { 72,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXC */
        { 77,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD3 */
+       { 78,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD2 */
+       { 79,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD1 */
        { 80,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXD0 */
-       { 94,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD0 */
-       { 93,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD1 */
-       { 92,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD2 */
-       { 91,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD3 */
+       { 81,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXCTL */
        { 85,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXC */
+       { 91,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD3 */
+       { 92,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD2 */
+       { 93,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD1 */
+       { 94,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXD0 */
        { 95,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_TXCTL */
-       { 72,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXC */
-       { 81,   BUFFER_CLASS_D | PIN_PDIS | MODE(1) },  /* RGMII_RXCTL */
 
        /* MDIO */
-       { 99,   BUFFER_CLASS_B | PIN_PDIS | MODE(0) },  /* MDIO_CLK */
        { 98,   BUFFER_CLASS_B | PIN_PDIS | MODE(0) },  /* MDIO_DATA */
+       { 99,   BUFFER_CLASS_B | PIN_PDIS | MODE(0) },  /* MDIO_CLK */
 
        { MAX_PIN_N, }
 };
index 184868063abdab7a6a1cbd6d084ebeb20af81f67..efe8c9ef09540d91b628cefb5bec7316e55a8372 100644 (file)
@@ -39,21 +39,24 @@ static int extract_range(char *input, int *plo, int *phi)
        return 0;
 }
 
-static int mdio_write_ranges(struct phy_device *phydev, struct mii_dev *bus,
+static int mdio_write_ranges(struct mii_dev *bus,
                             int addrlo,
                             int addrhi, int devadlo, int devadhi,
                             int reglo, int reghi, unsigned short data,
                             int extended)
 {
+       struct phy_device *phydev;
        int addr, devad, reg;
        int err = 0;
 
        for (addr = addrlo; addr <= addrhi; addr++) {
+               phydev = bus->phymap[addr];
+
                for (devad = devadlo; devad <= devadhi; devad++) {
                        for (reg = reglo; reg <= reghi; reg++) {
                                if (!extended)
-                                       err = bus->write(bus, addr, devad,
-                                                        reg, data);
+                                       err = phy_write_mmd(phydev, devad,
+                                                           reg, data);
                                else
                                        err = phydev->drv->writeext(phydev,
                                                        addr, devad, reg, data);
@@ -68,15 +71,17 @@ err_out:
        return err;
 }
 
-static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus,
+static int mdio_read_ranges(struct mii_dev *bus,
                            int addrlo,
                            int addrhi, int devadlo, int devadhi,
                            int reglo, int reghi, int extended)
 {
        int addr, devad, reg;
+       struct phy_device *phydev;
 
        printf("Reading from bus %s\n", bus->name);
        for (addr = addrlo; addr <= addrhi; addr++) {
+               phydev = bus->phymap[addr];
                printf("PHY at address %x:\n", addr);
 
                for (devad = devadlo; devad <= devadhi; devad++) {
@@ -84,7 +89,7 @@ static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus,
                                int val;
 
                                if (!extended)
-                                       val = bus->read(bus, addr, devad, reg);
+                                       val = phy_read_mmd(phydev, devad, reg);
                                else
                                        val = phydev->drv->readext(phydev, addr,
                                                devad, reg);
@@ -222,14 +227,14 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                                bus = phydev->bus;
                                extended = 1;
                        } else {
-                               return -1;
+                               return CMD_RET_FAILURE;
                        }
 
                        if (!phydev->drv ||
                            (!phydev->drv->writeext && (op[0] == 'w')) ||
                            (!phydev->drv->readext && (op[0] == 'r'))) {
                                puts("PHY does not have extended functions\n");
-                               return -1;
+                               return CMD_RET_FAILURE;
                        }
                }
        }
@@ -242,13 +247,13 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                if (pos > 1)
                        if (extract_reg_range(argv[pos--], &devadlo, &devadhi,
                                              &reglo, &reghi))
-                               return -1;
+                               return CMD_RET_FAILURE;
 
        default:
                if (pos > 1)
                        if (extract_phy_range(&argv[2], pos - 1, &bus,
                                              &phydev, &addrlo, &addrhi))
-                               return -1;
+                               return CMD_RET_FAILURE;
 
                break;
        }
@@ -264,12 +269,12 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 
        switch (op[0]) {
        case 'w':
-               mdio_write_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi,
+               mdio_write_ranges(bus, addrlo, addrhi, devadlo, devadhi,
                                  reglo, reghi, data, extended);
                break;
 
        case 'r':
-               mdio_read_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi,
+               mdio_read_ranges(bus, addrlo, addrhi, devadlo, devadhi,
                                 reglo, reghi, extended);
                break;
        }
index 307f53f726c16c58766cc7301f156230c0ab4507..a214d35fc9005854b1f534c792a88207b6fcf7e7 100644 (file)
@@ -14,6 +14,33 @@ KSZ9021:
   value is 0, the maximum value is 1800, and it is incremented by 120ps
   steps.
 
+  The KSZ9021 hardware supports a range of skew values from negative to
+  positive, where the specific range is property dependent. All values
+  specified in the devicetree are offset by the minimum value so they
+  can be represented as positive integers in the devicetree since it's
+  difficult to represent a negative number in the devictree.
+
+  The following 4-bit values table applies to all the skew properties:
+
+  Pad Skew Value       Delay (ps)      Devicetree Value
+  ------------------------------------------------------
+  0000                 -840ps          0
+  0001                 -720ps          120
+  0010                 -600ps          240
+  0011                 -480ps          360
+  0100                 -360ps          480
+  0101                 -240ps          600
+  0110                 -120ps          720
+  0111                 0ps             840
+  1000                 120ps           960
+  1001                 240ps           1080
+  1010                 360ps           1200
+  1011                 480ps           1320
+  1100                 600ps           1440
+  1101                 720ps           1560
+  1110                 840ps           1680
+  1111                 960ps           1800
+
   Optional properties:
 
     - rxc-skew-ps : Skew control of RXC pad
index 73b7ba29dfdc031ff5fc9e30c11a23f1d599c970..34253e39249d38ffec6725d18aa63c8f275f4b78 100644 (file)
@@ -1074,6 +1074,7 @@ int ldpaa_eth_init(int dpmac_id, phy_interface_t enet_if)
        priv = (struct ldpaa_eth_priv *)malloc(sizeof(struct ldpaa_eth_priv));
        if (!priv) {
                printf("ldpaa_eth_priv malloc() failed\n");
+               free(net_dev);
                return -ENOMEM;
        }
        memset(priv, 0, sizeof(struct ldpaa_eth_priv));
index 3dc0822d9c21cfa83591be5791fcdd7a5066a7c6..631b52b1cfe2634a11e828395cd98ffd90fd650b 100644 (file)
@@ -202,6 +202,26 @@ config RTL8211X_PHY_FORCE_MASTER
 
          If unsure, say N.
 
+config RTL8211F_PHY_FORCE_EEE_RXC_ON
+       bool "Ethernet PHY RTL8211F: do not stop receiving the xMII clock during LPI"
+       depends on PHY_REALTEK
+       default n
+       help
+         The IEEE 802.3az-2010 (EEE) standard provides a protocol to coordinate
+         transitions to/from a lower power consumption level (Low Power Idle
+         mode) based on link utilization. When no packets are being
+         transmitted, the system goes to Low Power Idle mode to save power.
+
+         Under particular circumstances this setting can cause issues where
+         the PHY is unable to transmit or receive any packet when in LPI mode.
+         The problem is caused when the PHY is configured to stop receiving
+         the xMII clock while it is signaling LPI. For some PHYs the bit
+         configuring this behavior is set by the Linux kernel, causing the
+         issue in U-Boot on reboot if the PHY retains the register value.
+
+         Default n, which means that the PHY state is not changed. To work
+         around the issues, change this setting to y.
+
 config PHY_SMSC
        bool  "Microchip(SMSC) Ethernet PHYs support"
 
index 12df09877de9994c2f1f1e5a03bf3390393c2bdd..5c3298d612c23e9b159f6c411b4d51517ffdb8ad 100644 (file)
@@ -303,9 +303,14 @@ int aquantia_config(struct phy_device *phydev)
                               AQUANTIA_SYSTEM_INTERFACE_SR);
                /* If SI is USXGMII then start USXGMII autoneg */
                if ((val & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII) {
+                       reg_val1 =  phy_read(phydev, MDIO_MMD_PHYXS,
+                                            AQUANTIA_VENDOR_PROVISIONING_REG);
+
+                       reg_val1 |= AQUANTIA_USX_AUTONEG_CONTROL_ENA;
+
                        phy_write(phydev, MDIO_MMD_PHYXS,
                                  AQUANTIA_VENDOR_PROVISIONING_REG,
-                                 AQUANTIA_USX_AUTONEG_CONTROL_ENA);
+                                 reg_val1);
                        printf("%s: system interface USXGMII\n",
                               phydev->dev->name);
                } else {
index 63e7b0242b92787922ba70b8498ca1f473142539..8dec9f236780a282abe29e9dd96cdbb5e033d6e1 100644 (file)
 #define CTRL1000_CONFIG_MASTER         (1 << 11)
 #define CTRL1000_MANUAL_CONFIG         (1 << 12)
 
+#define KSZ9021_PS_TO_REG              120
+
 /* KSZ9031 PHY Registers */
 #define MII_KSZ9031_MMD_ACCES_CTRL     0x0d
 #define MII_KSZ9031_MMD_REG_DATA       0x0e
 
+#define KSZ9031_PS_TO_REG              60
+
 static int ksz90xx_startup(struct phy_device *phydev)
 {
        unsigned phy_ctl;
@@ -102,20 +106,28 @@ static const struct ksz90x1_reg_field ksz9031_clk_grp[] = {
 };
 
 static int ksz90x1_of_config_group(struct phy_device *phydev,
-                                  struct ksz90x1_ofcfg *ofcfg)
+                                  struct ksz90x1_ofcfg *ofcfg,
+                                  int ps_to_regval)
 {
        struct udevice *dev = phydev->dev;
        struct phy_driver *drv = phydev->drv;
-       const int ps_to_regval = 60;
        int val[4];
        int i, changed = 0, offset, max;
        u16 regval = 0;
+       ofnode node;
 
        if (!drv || !drv->writeext)
                return -EOPNOTSUPP;
 
+       /* Look for a PHY node under the Ethernet node */
+       node = dev_read_subnode(dev, "ethernet-phy");
+       if (!ofnode_valid(node)) {
+               /* No node found, look in the Ethernet node */
+               node = dev_ofnode(dev);
+       }
+
        for (i = 0; i < ofcfg->grpsz; i++) {
-               val[i] = dev_read_u32_default(dev, ofcfg->grp[i].name, ~0);
+               val[i] = ofnode_read_u32_default(node, ofcfg->grp[i].name, ~0);
                offset = ofcfg->grp[i].off;
                if (val[i] == -1) {
                        /* Default register value for KSZ9021 */
@@ -148,7 +160,8 @@ static int ksz9021_of_config(struct phy_device *phydev)
        int i, ret = 0;
 
        for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
-               ret = ksz90x1_of_config_group(phydev, &(ofcfg[i]));
+               ret = ksz90x1_of_config_group(phydev, &ofcfg[i],
+                                             KSZ9021_PS_TO_REG);
                if (ret)
                        return ret;
        }
@@ -167,7 +180,8 @@ static int ksz9031_of_config(struct phy_device *phydev)
        int i, ret = 0;
 
        for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
-               ret = ksz90x1_of_config_group(phydev, &(ofcfg[i]));
+               ret = ksz90x1_of_config_group(phydev, &ofcfg[i],
+                                             KSZ9031_PS_TO_REG);
                if (ret)
                        return ret;
        }
index 4e8d2943eee1cf1c95c06fcfd60c860a92f85e44..c1c1af9abdbe9a8a60c4de06bfea921fd6b3df12 100644 (file)
@@ -462,6 +462,18 @@ static LIST_HEAD(phy_drivers);
 
 int phy_init(void)
 {
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+       /*
+        * The pointers inside phy_drivers also needs to be updated incase of
+        * manual reloc, without which these points to some invalid
+        * pre reloc address and leads to invalid accesses, hangs.
+        */
+       struct list_head *head = &phy_drivers;
+
+       head->next = (void *)head->next + gd->reloc_off;
+       head->prev = (void *)head->prev + gd->reloc_off;
+#endif
+
 #ifdef CONFIG_B53_SWITCH
        phy_b53_init();
 #endif
@@ -549,6 +561,10 @@ int phy_register(struct phy_driver *drv)
                drv->readext += gd->reloc_off;
        if (drv->writeext)
                drv->writeext += gd->reloc_off;
+       if (drv->read_mmd)
+               drv->read_mmd += gd->reloc_off;
+       if (drv->write_mmd)
+               drv->write_mmd += gd->reloc_off;
 #endif
        return 0;
 }
@@ -655,7 +671,10 @@ static struct phy_device *phy_device_create(struct mii_dev *bus, int addr,
 
        dev->drv = get_phy_driver(dev, interface);
 
-       phy_probe(dev);
+       if (phy_probe(dev)) {
+               printf("%s, PHY probe failed\n", __func__);
+               return NULL;
+       }
 
        if (addr >= 0 && addr < PHY_MAX_ADDR)
                bus->phymap[addr] = dev;
index dd45e11b3ad9852ef2cf9afada767dce6ab7a9bd..8f1d75963259848490b66af7a4453e628b8177ab 100644 (file)
@@ -12,6 +12,7 @@
 
 #define PHY_RTL8211x_FORCE_MASTER BIT(1)
 #define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2)
+#define PHY_RTL8211F_FORCE_EEE_RXC_ON BIT(3)
 
 #define PHY_AUTONEGOTIATE_TIMEOUT 5000
 
@@ -102,6 +103,15 @@ static int rtl8211e_probe(struct phy_device *phydev)
        return 0;
 }
 
+static int rtl8211f_probe(struct phy_device *phydev)
+{
+#ifdef CONFIG_RTL8211F_PHY_FORCE_EEE_RXC_ON
+       phydev->flags |= PHY_RTL8211F_FORCE_EEE_RXC_ON;
+#endif
+
+       return 0;
+}
+
 /* RealTek RTL8211x */
 static int rtl8211x_config(struct phy_device *phydev)
 {
@@ -151,6 +161,14 @@ static int rtl8211f_config(struct phy_device *phydev)
 {
        u16 reg;
 
+       if (phydev->flags & PHY_RTL8211F_FORCE_EEE_RXC_ON) {
+               unsigned int reg;
+
+               reg = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+               reg &= ~MDIO_PCS_CTRL1_CLKSTOP_EN;
+               phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+       }
+
        phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
 
        phy_write(phydev, MDIO_DEVAD_NONE,
@@ -360,6 +378,7 @@ static struct phy_driver RTL8211F_driver = {
        .uid = 0x1cc916,
        .mask = 0xffffff,
        .features = PHY_GBIT_FEATURES,
+       .probe = &rtl8211f_probe,
        .config = &rtl8211f_config,
        .startup = &rtl8211f_startup,
        .shutdown = &genphy_shutdown,
index 6db6edd0d0c844caedb903ebe21d6092e16a4e38..6ac890a7f5c33d082f006cca1734a6e51f5db189 100644 (file)
 #define MII_DP83867_CFG2_SPEEDOPT_INTLOW       0x2000
 #define MII_DP83867_CFG2_MASK                  0x003F
 
-#define MII_MMD_CTRL   0x0d /* MMD Access Control Register */
-#define MII_MMD_DATA   0x0e /* MMD Access Data Register */
-
-/* MMD Access Control register fields */
-#define MII_MMD_CTRL_DEVAD_MASK        0x1f /* Mask MMD DEVAD*/
-#define MII_MMD_CTRL_ADDR      0x0000 /* Address */
-#define MII_MMD_CTRL_NOINCR    0x4000 /* no post increment */
-#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */
-#define MII_MMD_CTRL_INCR_ON_WT        0xC000 /* post increment on writes only */
-
 /* User setting - can be taken from DTS */
 #define DEFAULT_RX_ID_DELAY    DP83867_RGMIIDCTL_2_25_NS
 #define DEFAULT_TX_ID_DELAY    DP83867_RGMIIDCTL_2_75_NS
@@ -116,88 +106,20 @@ struct dp83867_private {
        int clk_output_sel;
 };
 
-/**
- * phy_read_mmd_indirect - reads data from the MMD registers
- * @phydev: The PHY device bus
- * @prtad: MMD Address
- * @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
- *
- * Description: it reads data from the MMD registers (clause 22 to access to
- * clause 45) of the specified phy address.
- * To read these registers we have:
- * 1) Write reg 13 // DEVAD
- * 2) Write reg 14 // MMD Address
- * 3) Write reg 13 // MMD Data Command for MMD DEVAD
- * 3) Read  reg 14 // Read MMD data
- */
-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
-                         int devad, int addr)
-{
-       int value = -1;
-
-       /* Write the desired MMD Devad */
-       phy_write(phydev, addr, MII_MMD_CTRL, devad);
-
-       /* Write the desired MMD register address */
-       phy_write(phydev, addr, MII_MMD_DATA, prtad);
-
-       /* Select the Function : DATA with no post increment */
-       phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
-
-       /* Read the content of the MMD's selected register */
-       value = phy_read(phydev, addr, MII_MMD_DATA);
-       return value;
-}
-
-/**
- * phy_write_mmd_indirect - writes data to the MMD registers
- * @phydev: The PHY device
- * @prtad: MMD Address
- * @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
- * @data: data to write in the MMD register
- *
- * Description: Write data from the MMD registers of the specified
- * phy address.
- * To write these registers we have:
- * 1) Write reg 13 // DEVAD
- * 2) Write reg 14 // MMD Address
- * 3) Write reg 13 // MMD Data Command for MMD DEVAD
- * 3) Write reg 14 // Write MMD data
- */
-void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
-                           int devad, int addr, u32 data)
-{
-       /* Write the desired MMD Devad */
-       phy_write(phydev, addr, MII_MMD_CTRL, devad);
-
-       /* Write the desired MMD register address */
-       phy_write(phydev, addr, MII_MMD_DATA, prtad);
-
-       /* Select the Function : DATA with no post increment */
-       phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
-
-       /* Write the data into MMD's selected register */
-       phy_write(phydev, addr, MII_MMD_DATA, data);
-}
-
 static int dp83867_config_port_mirroring(struct phy_device *phydev)
 {
        struct dp83867_private *dp83867 =
                (struct dp83867_private *)phydev->priv;
        u16 val;
 
-       val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR,
-                                   phydev->addr);
+       val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4);
 
        if (dp83867->port_mirroring == DP83867_PORT_MIRRORING_EN)
                val |= DP83867_CFG4_PORT_MIRROR_EN;
        else
                val &= ~DP83867_CFG4_PORT_MIRROR_EN;
 
-       phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR,
-                              phydev->addr, val);
+       phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);
 
        return 0;
 }
@@ -257,13 +179,13 @@ static int dp83867_of_init(struct phy_device *phydev)
 
        /* Clock output selection if muxing property is set */
        if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) {
-               val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG,
-                                           DP83867_DEVADDR, phydev->addr);
+               val = phy_read_mmd(phydev, DP83867_DEVADDR,
+                                  DP83867_IO_MUX_CFG);
                val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
                val |= (dp83867->clk_output_sel <<
                        DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
-               phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG,
-                                      DP83867_DEVADDR, phydev->addr, val);
+               phy_write_mmd(phydev, DP83867_DEVADDR,
+                             DP83867_IO_MUX_CFG, val);
        }
 
        return 0;
@@ -308,11 +230,11 @@ static int dp83867_config(struct phy_device *phydev)
 
        /* Mode 1 or 2 workaround */
        if (dp83867->rxctrl_strap_quirk) {
-               val = phy_read_mmd_indirect(phydev, DP83867_CFG4,
-                                           DP83867_DEVADDR, phydev->addr);
+               val = phy_read_mmd(phydev, DP83867_DEVADDR,
+                                  DP83867_CFG4);
                val &= ~BIT(7);
-               phy_write_mmd_indirect(phydev, DP83867_CFG4,
-                                      DP83867_DEVADDR, phydev->addr, val);
+               phy_write_mmd(phydev, DP83867_DEVADDR,
+                             DP83867_CFG4, val);
        }
 
        if (phy_interface_is_rgmii(phydev)) {
@@ -332,8 +254,8 @@ static int dp83867_config(struct phy_device *phydev)
                 * register's bit 11 (marked as RESERVED).
                 */
 
-               bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1,
-                                          DP83867_DEVADDR, phydev->addr);
+               bs = phy_read_mmd(phydev, DP83867_DEVADDR,
+                                 DP83867_STRAP_STS1);
                val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
                if (bs & DP83867_STRAP_STS1_RESERVED) {
                        val &= ~DP83867_PHYCR_RESERVED_MASK;
@@ -354,8 +276,8 @@ static int dp83867_config(struct phy_device *phydev)
                         MII_DP83867_CFG2_SPEEDOPT_INTLOW);
                phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG2, cfg2);
 
-               phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
-                                      DP83867_DEVADDR, phydev->addr, 0x0);
+               phy_write_mmd(phydev, DP83867_DEVADDR,
+                             DP83867_RGMIICTL, 0x0);
 
                phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL,
                          DP83867_PHYCTRL_SGMIIEN |
@@ -367,8 +289,8 @@ static int dp83867_config(struct phy_device *phydev)
        }
 
        if (phy_interface_is_rgmii(phydev)) {
-               val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL,
-                                           DP83867_DEVADDR, phydev->addr);
+               val = phy_read_mmd(phydev, DP83867_DEVADDR,
+                                  DP83867_RGMIICTL);
 
                if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
                        val |= (DP83867_RGMII_TX_CLK_DELAY_EN |
@@ -380,26 +302,24 @@ static int dp83867_config(struct phy_device *phydev)
                if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
                        val |= DP83867_RGMII_RX_CLK_DELAY_EN;
 
-               phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
-                                      DP83867_DEVADDR, phydev->addr, val);
+               phy_write_mmd(phydev, DP83867_DEVADDR,
+                             DP83867_RGMIICTL, val);
 
                delay = (dp83867->rx_id_delay |
                         (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
 
-               phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL,
-                                      DP83867_DEVADDR, phydev->addr, delay);
+               phy_write_mmd(phydev, DP83867_DEVADDR,
+                             DP83867_RGMIIDCTL, delay);
 
                if (dp83867->io_impedance >= 0) {
-                       val = phy_read_mmd_indirect(phydev,
-                                                   DP83867_IO_MUX_CFG,
-                                                   DP83867_DEVADDR,
-                                                   phydev->addr);
+                       val = phy_read_mmd(phydev,
+                                          DP83867_DEVADDR,
+                                          DP83867_IO_MUX_CFG);
                        val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
                        val |= dp83867->io_impedance &
                               DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
-                       phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG,
-                                              DP83867_DEVADDR, phydev->addr,
-                                              val);
+                       phy_write_mmd(phydev, DP83867_DEVADDR,
+                                     DP83867_IO_MUX_CFG, val);
                }
        }
 
index 102fb91fffd07cc0f65a8aef150247dae6459a15..957efb3984bf6f32a370e68807f8d0dab2d0d08a 100644 (file)
@@ -147,6 +147,14 @@ config MESON_GXL_USB_PHY
          This is the generic phy driver for the Amlogic Meson GXL
          USB2 and USB3 PHYS.
 
+config MESON_G12A_USB_PHY
+       bool "Amlogic Meson G12A USB PHYs"
+       depends on PHY && ARCH_MESON && MESON_G12A
+       imply REGMAP
+       help
+         This is the generic phy driver for the Amlogic Meson G12A
+         USB2 and USB3 PHYS.
+
 config MSM8916_USB_PHY
        bool "Qualcomm MSM8916 USB PHY support"
        depends on PHY
index b55917bce1ae62353fb4ace629c74e790fedf808..90646ca55b9d71ab964f73240db1816b6e2f883c 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
 obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o
 obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o
 obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o
+obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o
 obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o
 obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o
 obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o
diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c
new file mode 100644 (file)
index 0000000..ad1a77f
--- /dev/null
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson G12A USB2 PHY driver
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstron@baylibre.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <bitfield.h>
+#include <dm.h>
+#include <errno.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <power/regulator.h>
+#include <reset.h>
+#include <clk.h>
+
+#include <linux/bitops.h>
+#include <linux/compat.h>
+
+#define PHY_CTRL_R0                                            0x0
+#define PHY_CTRL_R1                                            0x4
+#define PHY_CTRL_R2                                            0x8
+#define PHY_CTRL_R3                                            0xc
+#define PHY_CTRL_R4                                            0x10
+#define PHY_CTRL_R5                                            0x14
+#define PHY_CTRL_R6                                            0x18
+#define PHY_CTRL_R7                                            0x1c
+#define PHY_CTRL_R8                                            0x20
+#define PHY_CTRL_R9                                            0x24
+#define PHY_CTRL_R10                                           0x28
+#define PHY_CTRL_R11                                           0x2c
+#define PHY_CTRL_R12                                           0x30
+#define PHY_CTRL_R13                                           0x34
+#define PHY_CTRL_R14                                           0x38
+#define PHY_CTRL_R15                                           0x3c
+#define PHY_CTRL_R16                                           0x40
+#define PHY_CTRL_R17                                           0x44
+#define PHY_CTRL_R18                                           0x48
+#define PHY_CTRL_R19                                           0x4c
+#define PHY_CTRL_R20                                           0x50
+#define PHY_CTRL_R21                                           0x54
+#define PHY_CTRL_R22                                           0x58
+#define PHY_CTRL_R23                                           0x5c
+
+#define RESET_COMPLETE_TIME                                    1000
+#define PLL_RESET_COMPLETE_TIME                                        100
+
+struct phy_meson_g12a_usb2_priv {
+       struct regmap           *regmap;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       struct udevice          *phy_supply;
+#endif
+#if CONFIG_IS_ENABLED(CLK)
+       struct clk              clk;
+#endif
+       struct reset_ctl        reset;
+};
+
+
+static int phy_meson_g12a_usb2_power_on(struct phy *phy)
+{
+       struct udevice *dev = phy->dev;
+       struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       if (priv->phy_supply) {
+               int ret = regulator_set_enable(priv->phy_supply, true);
+               if (ret)
+                       return ret;
+       }
+#endif
+
+       return 0;
+}
+
+static int phy_meson_g12a_usb2_power_off(struct phy *phy)
+{
+       struct udevice *dev = phy->dev;
+       struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       if (priv->phy_supply) {
+               int ret = regulator_set_enable(priv->phy_supply, false);
+               if (ret) {
+                       pr_err("Error disabling PHY supply\n");
+                       return ret;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+static int phy_meson_g12a_usb2_init(struct phy *phy)
+{
+       struct udevice *dev = phy->dev;
+       struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = reset_assert(&priv->reset);
+       udelay(1);
+       ret |= reset_deassert(&priv->reset);
+       if (ret)
+               return ret;
+
+       udelay(RESET_COMPLETE_TIME);
+
+       /* usb2_otg_aca_en == 0 */
+       regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
+
+       /* PLL Setup : 24MHz * 20 / 1 = 480MHz */
+       regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
+       regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
+       regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
+
+       udelay(PLL_RESET_COMPLETE_TIME);
+
+       /* UnReset PLL */
+       regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
+
+       /* PHY Tuning */
+       regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18);
+       regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff);
+
+       /* Tuning Disconnect Threshold */
+       regmap_write(priv->regmap, PHY_CTRL_R3, 0x34);
+
+       /* Analog Settings */
+       regmap_write(priv->regmap, PHY_CTRL_R14, 0);
+       regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000);
+
+       return 0;
+}
+
+static int phy_meson_g12a_usb2_exit(struct phy *phy)
+{
+       struct udevice *dev = phy->dev;
+       struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = reset_assert(&priv->reset);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct phy_ops meson_g12a_usb2_phy_ops = {
+       .init = phy_meson_g12a_usb2_init,
+       .exit = phy_meson_g12a_usb2_exit,
+       .power_on = phy_meson_g12a_usb2_power_on,
+       .power_off = phy_meson_g12a_usb2_power_off,
+};
+
+int meson_g12a_usb2_phy_probe(struct udevice *dev)
+{
+       struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+       if (ret)
+               return ret;
+
+       ret = reset_get_by_index(dev, 0, &priv->reset);
+       if (ret == -ENOTSUPP)
+               return 0;
+       else if (ret)
+               return ret;
+
+       ret = reset_deassert(&priv->reset);
+       if (ret) {
+               reset_release_all(&priv->reset, 1);
+               return ret;
+       }
+
+#if CONFIG_IS_ENABLED(CLK)
+       ret = clk_get_by_index(dev, 0, &priv->clk);
+       if (ret < 0)
+               return ret;
+
+       ret = clk_enable(&priv->clk);
+       if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
+               pr_err("failed to enable PHY clock\n");
+               clk_free(&priv->clk);
+               return ret;
+       }
+#endif
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       ret = device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply);
+       if (ret && ret != -ENOENT) {
+               pr_err("Failed to get PHY regulator\n");
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+
+static const struct udevice_id meson_g12a_usb2_phy_ids[] = {
+       { .compatible = "amlogic,g12a-usb2-phy" },
+       { }
+};
+
+U_BOOT_DRIVER(meson_g12a_usb2_phy) = {
+       .name = "meson_g12a_usb2_phy",
+       .id = UCLASS_PHY,
+       .of_match = meson_g12a_usb2_phy_ids,
+       .probe = meson_g12a_usb2_phy_probe,
+       .ops = &meson_g12a_usb2_phy_ops,
+       .priv_auto_alloc_size = sizeof(struct phy_meson_g12a_usb2_priv),
+};
diff --git a/drivers/phy/meson-g12a-usb3-pcie.c b/drivers/phy/meson-g12a-usb3-pcie.c
new file mode 100644 (file)
index 0000000..920675d
--- /dev/null
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson G12A USB3+PCIE Combo PHY driver
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstron@baylibre.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <regmap.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <reset.h>
+#include <bitfield.h>
+#include <generic-phy.h>
+
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/bitfield.h>
+
+#define PHY_R0                                                 0x00
+       #define PHY_R0_PCIE_POWER_STATE                         GENMASK(4, 0)
+       #define PHY_R0_PCIE_USB3_SWITCH                         GENMASK(6, 5)
+
+#define PHY_R1                                                 0x04
+       #define PHY_R1_PHY_TX1_TERM_OFFSET                      GENMASK(4, 0)
+       #define PHY_R1_PHY_TX0_TERM_OFFSET                      GENMASK(9, 5)
+       #define PHY_R1_PHY_RX1_EQ                               GENMASK(12, 10)
+       #define PHY_R1_PHY_RX0_EQ                               GENMASK(15, 13)
+       #define PHY_R1_PHY_LOS_LEVEL                            GENMASK(20, 16)
+       #define PHY_R1_PHY_LOS_BIAS                             GENMASK(23, 21)
+       #define PHY_R1_PHY_REF_CLKDIV2                          BIT(24)
+       #define PHY_R1_PHY_MPLL_MULTIPLIER                      GENMASK(31, 25)
+
+#define PHY_R2                                                 0x08
+       #define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB                   GENMASK(5, 0)
+       #define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB                 GENMASK(11, 6)
+       #define PHY_R2_PCS_TX_DEEMPH_GEN1                       GENMASK(17, 12)
+       #define PHY_R2_PHY_TX_VBOOST_LVL                        GENMASK(20, 18)
+
+#define PHY_R4                                                 0x10
+       #define PHY_R4_PHY_CR_WRITE                             BIT(0)
+       #define PHY_R4_PHY_CR_READ                              BIT(1)
+       #define PHY_R4_PHY_CR_DATA_IN                           GENMASK(17, 2)
+       #define PHY_R4_PHY_CR_CAP_DATA                          BIT(18)
+       #define PHY_R4_PHY_CR_CAP_ADDR                          BIT(19)
+
+#define PHY_R5                                                 0x14
+       #define PHY_R5_PHY_CR_DATA_OUT                          GENMASK(15, 0)
+       #define PHY_R5_PHY_CR_ACK                               BIT(16)
+       #define PHY_R5_PHY_BS_OUT                               BIT(17)
+
+struct phy_g12a_usb3_pcie_priv {
+       struct regmap           *regmap;
+#if CONFIG_IS_ENABLED(CLK)
+       struct clk              clk;
+#endif
+       struct reset_ctl_bulk   resets;
+};
+
+static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv,
+                                         unsigned int addr)
+{
+       unsigned int val, reg;
+       int ret;
+
+       reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr);
+
+       regmap_write(priv->regmap, PHY_R4, reg);
+       regmap_write(priv->regmap, PHY_R4, reg);
+
+       regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      (val & PHY_R5_PHY_CR_ACK),
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       regmap_write(priv->regmap, PHY_R4, reg);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      !(val & PHY_R5_PHY_CR_ACK),
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_read(struct phy_g12a_usb3_pcie_priv *priv,
+                              unsigned int addr, unsigned int *data)
+{
+       unsigned int val;
+       int ret;
+
+       ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+       if (ret)
+               return ret;
+
+       regmap_write(priv->regmap, PHY_R4, 0);
+       regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      (val & PHY_R5_PHY_CR_ACK),
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       *data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val);
+
+       regmap_write(priv->regmap, PHY_R4, 0);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      !(val & PHY_R5_PHY_CR_ACK),
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_write(struct phy_g12a_usb3_pcie_priv *priv,
+                               unsigned int addr, unsigned int data)
+{
+       unsigned int val, reg;
+       int ret;
+
+       ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+       if (ret)
+               return ret;
+
+       reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data);
+
+       regmap_write(priv->regmap, PHY_R4, reg);
+       regmap_write(priv->regmap, PHY_R4, reg);
+
+       regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      (val & PHY_R5_PHY_CR_ACK),
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       regmap_write(priv->regmap, PHY_R4, reg);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      (val & PHY_R5_PHY_CR_ACK) == 0,
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       regmap_write(priv->regmap, PHY_R4, reg);
+
+       regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      (val & PHY_R5_PHY_CR_ACK),
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       regmap_write(priv->regmap, PHY_R4, reg);
+
+       ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+                                      (val & PHY_R5_PHY_CR_ACK) == 0,
+                                      5, 1000);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_update_bits(struct phy_g12a_usb3_pcie_priv *priv,
+                                     uint offset, uint mask, uint val)
+{
+       uint reg;
+       int ret;
+
+       ret = phy_g12a_usb3_pcie_cr_bus_read(priv, offset, &reg);
+       if (ret)
+               return ret;
+
+       reg &= ~mask;
+
+       return phy_g12a_usb3_pcie_cr_bus_write(priv, offset, reg | val);
+}
+
+static int phy_meson_g12a_usb3_init(struct phy *phy)
+{
+       struct udevice *dev = phy->dev;
+       struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
+       unsigned int data;
+       int ret;
+
+       /* TOFIX Handle PCIE mode */
+
+       ret = reset_assert_bulk(&priv->resets);
+       udelay(1);
+       ret |= reset_deassert_bulk(&priv->resets);
+       if (ret)
+               return ret;
+
+       /* Switch PHY to USB3 */
+       regmap_update_bits(priv->regmap, PHY_R0,
+                          PHY_R0_PCIE_USB3_SWITCH,
+                          PHY_R0_PCIE_USB3_SWITCH);
+
+       /*
+        * WORKAROUND: There is SSPHY suspend bug due to
+        * which USB enumerates
+        * in HS mode instead of SS mode. Workaround it by asserting
+        * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus
+        * mode
+        */
+       ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x102d,
+                                                   BIT(7), BIT(7));
+       if (ret)
+               return ret;
+
+       ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x1010, 0xff0, 20);
+       if (ret)
+               return ret;
+
+       /*
+        * Fix RX Equalization setting as follows
+        * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
+        * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
+        * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
+        * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
+        */
+       ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1006, &data);
+       if (ret)
+               return ret;
+
+       data &= ~BIT(6);
+       data |= BIT(7);
+       data &= ~(0x7 << 8);
+       data |= (0x3 << 8);
+       data |= (1 << 11);
+       ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1006, data);
+       if (ret)
+               return ret;
+
+       /*
+        * Set EQ and TX launch amplitudes as follows
+        * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
+        * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
+        * LANE0.TX_OVRD_DRV_LO.EN set to 1.
+        */
+       ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1002, &data);
+       if (ret)
+               return ret;
+
+       data &= ~0x3f80;
+       data |= (0x16 << 7);
+       data &= ~0x7f;
+       data |= (0x7f | BIT(14));
+       ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1002, data);
+       if (ret)
+               return ret;
+
+       /*
+        * MPLL_LOOP_CTL.PROP_CNTRL = 8
+        */
+       ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x30,
+                                                   0xf << 4, 8 << 4);
+       if (ret)
+               return ret;
+
+       regmap_update_bits(priv->regmap, PHY_R2,
+                       PHY_R2_PHY_TX_VBOOST_LVL,
+                       FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4));
+
+       regmap_update_bits(priv->regmap, PHY_R1,
+                       PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL,
+                       FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) |
+                       FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9));
+
+       return ret;
+}
+
+static int phy_meson_g12a_usb3_exit(struct phy *phy)
+{
+       struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(phy->dev);
+
+       return reset_assert_bulk(&priv->resets);
+}
+
+struct phy_ops meson_g12a_usb3_pcie_phy_ops = {
+       .init = phy_meson_g12a_usb3_init,
+       .exit = phy_meson_g12a_usb3_exit,
+};
+
+int meson_g12a_usb3_pcie_phy_probe(struct udevice *dev)
+{
+       struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+       if (ret)
+               return ret;
+
+       ret = reset_get_bulk(dev, &priv->resets);
+       if (ret == -ENOTSUPP)
+               return 0;
+       else if (ret)
+               return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+       ret = clk_get_by_index(dev, 0, &priv->clk);
+       if (ret < 0)
+               return ret;
+
+       ret = clk_enable(&priv->clk);
+       if (ret && ret != -ENOENT && ret != -ENOTSUPP) {
+               pr_err("failed to enable PHY clock\n");
+               clk_free(&priv->clk);
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+
+static const struct udevice_id meson_g12a_usb3_pcie_phy_ids[] = {
+       { .compatible = "amlogic,g12a-usb3-pcie-phy" },
+       { }
+};
+
+U_BOOT_DRIVER(meson_g12a_usb3_pcie_phy) = {
+       .name = "meson_g12a_usb3_pcie_phy",
+       .id = UCLASS_PHY,
+       .of_match = meson_g12a_usb3_pcie_phy_ids,
+       .probe = meson_g12a_usb3_pcie_phy_probe,
+       .ops = &meson_g12a_usb3_pcie_phy_ops,
+       .priv_auto_alloc_size = sizeof(struct phy_g12a_usb3_pcie_priv),
+};
index bbd8105c06f2917d0acae857a97f611d22517678..25e1a38aee08fbb87a96dddeb733b11553fc6b5e 100644 (file)
@@ -44,6 +44,14 @@ config USB_DWC3_GENERIC
          Select this for Xilinx ZynqMP and similar Platforms.
          This wrapper supports Host and Peripheral operation modes.
 
+config USB_DWC3_MESON_G12A
+       bool "Amlogic Meson G12A USB wrapper"
+       depends on DM_USB && USB_DWC3 && ARCH_MESON
+       imply PHY
+       help
+         Select this for Amlogic Meson G12A Platforms.
+         This wrapper supports Host and Peripheral operation modes.
+
 config USB_DWC3_UNIPHIER
        bool "DesignWare USB3 Host Support on UniPhier Platforms"
        depends on ARCH_UNIPHIER && USB_XHCI_DWC3
index 60b5515a67da9f4e7a35b1e8261df84124bd3330..0b652a6f3646250277998bea99f9bd2b401eaf22 100644 (file)
@@ -7,6 +7,7 @@ dwc3-y                                  := core.o
 obj-$(CONFIG_USB_DWC3_GADGET)          += gadget.o ep0.o
 
 obj-$(CONFIG_USB_DWC3_OMAP)            += dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_MESON_G12A)      += dwc3-meson-g12a.o
 obj-$(CONFIG_USB_DWC3_GENERIC)         += dwc3-generic.o
 obj-$(CONFIG_USB_DWC3_UNIPHIER)                += dwc3-uniphier.o
 obj-$(CONFIG_USB_DWC3_PHY_OMAP)                += ti_usb_phy.o
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
new file mode 100644 (file)
index 0000000..832bcd7
--- /dev/null
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic G12A DWC3 Glue layer
+ *
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <common.h>
+#include <asm-generic/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dwc3-uboot.h>
+#include <generic-phy.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <malloc.h>
+#include <regmap.h>
+#include <usb.h>
+#include "core.h"
+#include "gadget.h"
+#include <reset.h>
+#include <clk.h>
+#include <power/regulator.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+
+/* USB2 Ports Control Registers */
+
+#define U2P_REG_SIZE                                           0x20
+
+#define U2P_R0                                                 0x0
+       #define U2P_R0_HOST_DEVICE                              BIT(0)
+       #define U2P_R0_POWER_OK                                 BIT(1)
+       #define U2P_R0_HAST_MODE                                BIT(2)
+       #define U2P_R0_POWER_ON_RESET                           BIT(3)
+       #define U2P_R0_ID_PULLUP                                BIT(4)
+       #define U2P_R0_DRV_VBUS                                 BIT(5)
+
+#define U2P_R1                                                 0x4
+       #define U2P_R1_PHY_READY                                BIT(0)
+       #define U2P_R1_ID_DIG                                   BIT(1)
+       #define U2P_R1_OTG_SESSION_VALID                        BIT(2)
+       #define U2P_R1_VBUS_VALID                               BIT(3)
+
+/* USB Glue Control Registers */
+
+#define USB_R0                                                 0x80
+       #define USB_R0_P30_LANE0_TX2RX_LOOPBACK                 BIT(17)
+       #define USB_R0_P30_LANE0_EXT_PCLK_REQ                   BIT(18)
+       #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK             GENMASK(28, 19)
+       #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK               GENMASK(30, 29)
+       #define USB_R0_U2D_ACT                                  BIT(31)
+
+#define USB_R1                                                 0x84
+       #define USB_R1_U3H_BIGENDIAN_GS                         BIT(0)
+       #define USB_R1_U3H_PME_ENABLE                           BIT(1)
+       #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK            GENMASK(4, 2)
+       #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK            GENMASK(9, 7)
+       #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK            GENMASK(13, 12)
+       #define USB_R1_U3H_HOST_U3_PORT_DISABLE                 BIT(16)
+       #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT      BIT(17)
+       #define USB_R1_U3H_HOST_MSI_ENABLE                      BIT(18)
+       #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK                 GENMASK(24, 19)
+       #define USB_R1_P30_PCS_TX_SWING_FULL_MASK               GENMASK(31, 25)
+
+#define USB_R2                                                 0x88
+       #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK             GENMASK(25, 20)
+       #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK               GENMASK(31, 26)
+
+#define USB_R3                                                 0x8c
+       #define USB_R3_P30_SSC_ENABLE                           BIT(0)
+       #define USB_R3_P30_SSC_RANGE_MASK                       GENMASK(3, 1)
+       #define USB_R3_P30_SSC_REF_CLK_SEL_MASK                 GENMASK(12, 4)
+       #define USB_R3_P30_REF_SSP_EN                           BIT(13)
+
+#define USB_R4                                                 0x90
+       #define USB_R4_P21_PORT_RESET_0                         BIT(0)
+       #define USB_R4_P21_SLEEP_M0                             BIT(1)
+       #define USB_R4_MEM_PD_MASK                              GENMASK(3, 2)
+       #define USB_R4_P21_ONLY                                 BIT(4)
+
+#define USB_R5                                                 0x94
+       #define USB_R5_ID_DIG_SYNC                              BIT(0)
+       #define USB_R5_ID_DIG_REG                               BIT(1)
+       #define USB_R5_ID_DIG_CFG_MASK                          GENMASK(3, 2)
+       #define USB_R5_ID_DIG_EN_0                              BIT(4)
+       #define USB_R5_ID_DIG_EN_1                              BIT(5)
+       #define USB_R5_ID_DIG_CURR                              BIT(6)
+       #define USB_R5_ID_DIG_IRQ                               BIT(7)
+       #define USB_R5_ID_DIG_TH_MASK                           GENMASK(15, 8)
+       #define USB_R5_ID_DIG_CNT_MASK                          GENMASK(23, 16)
+
+enum {
+       USB2_HOST_PHY = 0,
+       USB2_OTG_PHY,
+       USB3_HOST_PHY,
+       PHY_COUNT,
+};
+
+static const char *phy_names[PHY_COUNT] = {
+       "usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+struct dwc3_meson_g12a {
+       struct udevice          *dev;
+       struct regmap           *regmap;
+       struct clk              clk;
+       struct reset_ctl        reset;
+       struct phy              phys[PHY_COUNT];
+       enum usb_dr_mode        otg_mode;
+       enum usb_dr_mode        otg_phy_mode;
+       unsigned int            usb2_ports;
+       unsigned int            usb3_ports;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       struct udevice          *vbus_supply;
+#endif
+};
+
+#define U2P_REG_SIZE                                           0x20
+#define USB_REG_OFFSET                                         0x80
+
+static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
+                                         int i, enum usb_dr_mode mode)
+{
+       switch (mode) {
+       case USB_DR_MODE_HOST:
+       case USB_DR_MODE_OTG:
+       case USB_DR_MODE_UNKNOWN:
+               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+                               U2P_R0_HOST_DEVICE,
+                               U2P_R0_HOST_DEVICE);
+               break;
+
+       case USB_DR_MODE_PERIPHERAL:
+               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+                               U2P_R0_HOST_DEVICE, 0);
+               break;
+       }
+}
+
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+{
+       int i;
+
+       if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+               priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL;
+       else
+               priv->otg_phy_mode = USB_DR_MODE_HOST;
+
+       for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
+               if (!priv->phys[i].dev)
+                       continue;
+
+               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+                                  U2P_R0_POWER_ON_RESET,
+                                  U2P_R0_POWER_ON_RESET);
+
+               if (i == USB2_OTG_PHY) {
+                       regmap_update_bits(priv->regmap,
+                                          U2P_R0 + (U2P_REG_SIZE * i),
+                                          U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+                                          U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+
+                       dwc3_meson_g12a_usb2_set_mode(priv, i,
+                                                     priv->otg_phy_mode);
+               } else
+                       dwc3_meson_g12a_usb2_set_mode(priv, i,
+                                                     USB_DR_MODE_HOST);
+
+               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+                                  U2P_R0_POWER_ON_RESET, 0);
+       }
+
+       return 0;
+}
+
+static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
+{
+       regmap_update_bits(priv->regmap, USB_R3,
+                       USB_R3_P30_SSC_RANGE_MASK |
+                       USB_R3_P30_REF_SSP_EN,
+                       USB_R3_P30_SSC_ENABLE |
+                       FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
+                       USB_R3_P30_REF_SSP_EN);
+       udelay(2);
+
+       regmap_update_bits(priv->regmap, USB_R2,
+                       USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
+                       FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
+
+       regmap_update_bits(priv->regmap, USB_R2,
+                       USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
+                       FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
+
+       udelay(2);
+
+       regmap_update_bits(priv->regmap, USB_R1,
+                       USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
+                       USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
+
+       regmap_update_bits(priv->regmap, USB_R1,
+                       USB_R1_P30_PCS_TX_SWING_FULL_MASK,
+                       FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
+}
+
+static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv)
+{
+       if (priv->otg_phy_mode == USB_DR_MODE_PERIPHERAL) {
+               regmap_update_bits(priv->regmap, USB_R0,
+                               USB_R0_U2D_ACT, USB_R0_U2D_ACT);
+               regmap_update_bits(priv->regmap, USB_R0,
+                               USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
+               regmap_update_bits(priv->regmap, USB_R4,
+                               USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
+       } else {
+               regmap_update_bits(priv->regmap, USB_R0,
+                               USB_R0_U2D_ACT, 0);
+               regmap_update_bits(priv->regmap, USB_R4,
+                               USB_R4_P21_SLEEP_M0, 0);
+       }
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+       int ret;
+
+       ret = dwc3_meson_g12a_usb2_init(priv);
+       if (ret)
+               return ret;
+
+       regmap_update_bits(priv->regmap, USB_R1,
+                       USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+                       FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
+
+       regmap_update_bits(priv->regmap, USB_R5,
+                       USB_R5_ID_DIG_EN_0,
+                       USB_R5_ID_DIG_EN_0);
+       regmap_update_bits(priv->regmap, USB_R5,
+                       USB_R5_ID_DIG_EN_1,
+                       USB_R5_ID_DIG_EN_1);
+       regmap_update_bits(priv->regmap, USB_R5,
+                       USB_R5_ID_DIG_TH_MASK,
+                       FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
+
+       /* If we have an actual SuperSpeed port, initialize it */
+       if (priv->usb3_ports)
+               dwc3_meson_g12a_usb3_init(priv);
+
+       dwc3_meson_g12a_usb_init_mode(priv);
+
+       return 0;
+}
+
+int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode)
+{
+       struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+
+       if (!priv)
+               return -EINVAL;
+
+       if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL)
+               return -EINVAL;
+
+       if (!priv->phys[USB2_OTG_PHY].dev)
+               return -EINVAL;
+
+       if (mode == priv->otg_mode)
+               return 0;
+
+       if (mode == USB_DR_MODE_HOST)
+               debug("%s: switching to Host Mode\n", __func__);
+       else
+               debug("%s: switching to Device Mode\n", __func__);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       if (priv->vbus_supply) {
+               int ret = regulator_set_enable(priv->vbus_supply,
+                                       (mode == USB_DR_MODE_PERIPHERAL));
+               if (ret)
+                       return ret;
+       }
+#endif
+       priv->otg_phy_mode = mode;
+
+       dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+
+       dwc3_meson_g12a_usb_init_mode(priv);
+
+       return 0;
+}
+
+static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
+{
+       int i, ret;
+
+       for (i = 0 ; i < PHY_COUNT ; ++i) {
+               ret = generic_phy_get_by_name(priv->dev, phy_names[i],
+                                             &priv->phys[i]);
+               if (ret == -ENOENT)
+                       continue;
+
+               if (ret)
+                       return ret;
+
+               if (i == USB3_HOST_PHY)
+                       priv->usb3_ports++;
+               else
+                       priv->usb2_ports++;
+       }
+
+       debug("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
+       debug("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
+
+       return 0;
+}
+
+static int dwc3_meson_g12a_reset_init(struct dwc3_meson_g12a *priv)
+{
+       int ret;
+
+       ret = reset_get_by_index(priv->dev, 0, &priv->reset);
+       if (ret)
+               return ret;
+
+       ret = reset_assert(&priv->reset);
+       udelay(1);
+       ret |= reset_deassert(&priv->reset);
+       if (ret) {
+               reset_free(&priv->reset);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv)
+{
+       int ret;
+
+       ret = clk_get_by_index(priv->dev, 0, &priv->clk);
+       if (ret)
+               return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+       ret = clk_enable(&priv->clk);
+       if (ret) {
+               clk_free(&priv->clk);
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+
+static int dwc3_meson_g12a_probe(struct udevice *dev)
+{
+       struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+       int ret, i;
+
+       priv->dev = dev;
+
+       ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+       if (ret)
+               return ret;
+
+       ret = dwc3_meson_g12a_clk_init(priv);
+       if (ret)
+               return ret;
+
+       ret = dwc3_meson_g12a_reset_init(priv);
+       if (ret)
+               return ret;
+
+       ret = dwc3_meson_g12a_get_phys(priv);
+       if (ret)
+               return ret;
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+       ret = device_get_supply_regulator(dev, "vbus-supply",
+                                         &priv->vbus_supply);
+       if (ret && ret != -ENOENT) {
+               pr_err("Failed to get PHY regulator\n");
+               return ret;
+       }
+
+       if (priv->vbus_supply) {
+               ret = regulator_set_enable(priv->vbus_supply, true);
+               if (ret)
+                       return ret;
+       }
+#endif
+
+       priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
+
+       ret = dwc3_meson_g12a_usb_init(priv);
+       if (ret)
+               return ret;
+
+       for (i = 0 ; i < PHY_COUNT ; ++i) {
+               if (!priv->phys[i].dev)
+                       continue;
+
+               ret = generic_phy_init(&priv->phys[i]);
+               if (ret)
+                       goto err_phy_init;
+       }
+
+       return 0;
+
+err_phy_init:
+       for (i = 0 ; i < PHY_COUNT ; ++i) {
+               if (!priv->phys[i].dev)
+                       continue;
+
+                generic_phy_exit(&priv->phys[i]);
+       }
+
+       return ret;
+}
+
+static int dwc3_meson_g12a_remove(struct udevice *dev)
+{
+       struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+       int i;
+
+       reset_release_all(&priv->reset, 1);
+
+       clk_release_all(&priv->clk, 1);
+
+       for (i = 0 ; i < PHY_COUNT ; ++i) {
+               if (!priv->phys[i].dev)
+                       continue;
+
+                generic_phy_exit(&priv->phys[i]);
+       }
+
+       return dm_scan_fdt_dev(dev);
+}
+
+static const struct udevice_id dwc3_meson_g12a_ids[] = {
+       { .compatible = "amlogic,meson-g12a-usb-ctrl" },
+       { }
+};
+
+U_BOOT_DRIVER(dwc3_generic_wrapper) = {
+       .name   = "dwc3-meson-g12a",
+       .id     = UCLASS_SIMPLE_BUS,
+       .of_match = dwc3_meson_g12a_ids,
+       .probe = dwc3_meson_g12a_probe,
+       .remove = dwc3_meson_g12a_remove,
+       .platdata_auto_alloc_size = sizeof(struct dwc3_meson_g12a),
+
+};
index f23ca63f3b84d02d6227ae43e0be7bca22b292f7..d01435d1aa1a2a720de3cd7d930ba88e7eef46e0 100644 (file)
@@ -101,6 +101,14 @@ struct phy_driver {
        int (*readext)(struct phy_device *phydev, int addr, int devad, int reg);
        int (*writeext)(struct phy_device *phydev, int addr, int devad, int reg,
                        u16 val);
+
+       /* Phy specific driver override for reading a MMD register */
+       int (*read_mmd)(struct phy_device *phydev, int devad, int reg);
+
+       /* Phy specific driver override for writing a MMD register */
+       int (*write_mmd)(struct phy_device *phydev, int devad, int reg,
+                        u16 val);
+
        struct list_head list;
 };
 
@@ -165,6 +173,68 @@ static inline int phy_write(struct phy_device *phydev, int devad, int regnum,
        return bus->write(bus, phydev->addr, devad, regnum, val);
 }
 
+static inline void phy_mmd_start_indirect(struct phy_device *phydev, int devad,
+                                         int regnum)
+{
+       /* Write the desired MMD Devad */
+       phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_CTRL, devad);
+
+       /* Write the desired MMD register address */
+       phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA, regnum);
+
+       /* Select the Function : DATA with no post increment */
+       phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_CTRL,
+                 (devad | MII_MMD_CTRL_NOINCR));
+}
+
+static inline int phy_read_mmd(struct phy_device *phydev, int devad,
+                              int regnum)
+{
+       struct phy_driver *drv = phydev->drv;
+
+       if (regnum > (u16)~0 || devad > 32)
+               return -EINVAL;
+
+       /* driver-specific access */
+       if (drv->read_mmd)
+               return drv->read_mmd(phydev, devad, regnum);
+
+       /* direct C45 / C22 access */
+       if ((drv->features & PHY_10G_FEATURES) == PHY_10G_FEATURES ||
+           devad == MDIO_DEVAD_NONE || !devad)
+               return phy_read(phydev, devad, regnum);
+
+       /* indirect C22 access */
+       phy_mmd_start_indirect(phydev, devad, regnum);
+
+       /* Read the content of the MMD's selected register */
+       return phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA);
+}
+
+static inline int phy_write_mmd(struct phy_device *phydev, int devad,
+                               int regnum, u16 val)
+{
+       struct phy_driver *drv = phydev->drv;
+
+       if (regnum > (u16)~0 || devad > 32)
+               return -EINVAL;
+
+       /* driver-specific access */
+       if (drv->write_mmd)
+               return drv->write_mmd(phydev, devad, regnum, val);
+
+       /* direct C45 / C22 access */
+       if ((drv->features & PHY_10G_FEATURES) == PHY_10G_FEATURES ||
+           devad == MDIO_DEVAD_NONE || !devad)
+               return phy_write(phydev, devad, regnum, val);
+
+       /* indirect C22 access */
+       phy_mmd_start_indirect(phydev, devad, regnum);
+
+       /* Write the data into MMD's selected register */
+       return phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA, val);
+}
+
 #ifdef CONFIG_PHYLIB_10G
 extern struct phy_driver gen10g_driver;