diff options
27 files changed, 1456 insertions, 183 deletions
diff --git a/arch/arm/dts/sama5d3xcm.dtsi b/arch/arm/dts/sama5d3xcm.dtsi index 2cf9c3611d..d123057f30 100644 --- a/arch/arm/dts/sama5d3xcm.dtsi +++ b/arch/arm/dts/sama5d3xcm.dtsi @@ -44,28 +44,28 @@ 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>; }; }; }; diff --git a/arch/arm/dts/sama5d3xcm_cmp.dtsi b/arch/arm/dts/sama5d3xcm_cmp.dtsi index 77638c3cbe..332b057e0a 100644 --- a/arch/arm/dts/sama5d3xcm_cmp.dtsi +++ b/arch/arm/dts/sama5d3xcm_cmp.dtsi @@ -43,28 +43,28 @@ 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>; }; }; diff --git a/arch/arm/dts/socfpga_arria5_socdk.dts b/arch/arm/dts/socfpga_arria5_socdk.dts index 90e676e701..fa972e287f 100644 --- a/arch/arm/dts/socfpga_arria5_socdk.dts +++ b/arch/arm/dts/socfpga_arria5_socdk.dts @@ -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 { diff --git a/arch/arm/dts/socfpga_cyclone5_is1.dts b/arch/arm/dts/socfpga_cyclone5_is1.dts index 2d31412923..a769498791 100644 --- a/arch/arm/dts/socfpga_cyclone5_is1.dts +++ b/arch/arm/dts/socfpga_cyclone5_is1.dts @@ -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 { diff --git a/arch/arm/dts/socfpga_cyclone5_socdk.dts b/arch/arm/dts/socfpga_cyclone5_socdk.dts index 6f138b2b26..95c7619b8d 100644 --- a/arch/arm/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/dts/socfpga_cyclone5_socdk.dts @@ -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 { diff --git a/arch/arm/dts/socfpga_cyclone5_sockit.dts b/arch/arm/dts/socfpga_cyclone5_sockit.dts index c155ff02eb..90669cde45 100644 --- a/arch/arm/dts/socfpga_cyclone5_sockit.dts +++ b/arch/arm/dts/socfpga_cyclone5_sockit.dts @@ -128,9 +128,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 { /* GPIO 0..29 */ diff --git a/arch/arm/dts/socfpga_cyclone5_vining_fpga.dts b/arch/arm/dts/socfpga_cyclone5_vining_fpga.dts index 355b3dbf43..ac57f41cb5 100644 --- a/arch/arm/dts/socfpga_cyclone5_vining_fpga.dts +++ b/arch/arm/dts/socfpga_cyclone5_vining_fpga.dts @@ -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 index 0000000000..b794b5ce77 --- /dev/null +++ b/arch/arm/include/asm/arch-meson/usb.h @@ -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__ */ diff --git a/arch/arm/mach-meson/board-g12a.c b/arch/arm/mach-meson/board-g12a.c index fc3764b960..1652970fbd 100644 --- a/arch/arm/mach-meson/board-g12a.c +++ b/arch/arm/mach-meson/board-g12a.c @@ -12,7 +12,12 @@ #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 diff --git a/board/ti/ks2_evm/mux-k2g.h b/board/ti/ks2_evm/mux-k2g.h index 89c49f9e4f..6aa785ea42 100644 --- a/board/ti/ks2_evm/mux-k2g.h +++ b/board/ti/ks2_evm/mux-k2g.h @@ -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, } }; diff --git a/cmd/mdio.c b/cmd/mdio.c index 184868063a..efe8c9ef09 100644 --- a/cmd/mdio.c +++ b/cmd/mdio.c @@ -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, ®lo, ®hi)) - 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; } diff --git a/doc/device-tree-bindings/net/micrel-ksz90x1.txt b/doc/device-tree-bindings/net/micrel-ksz90x1.txt index 307f53f726..a214d35fc9 100644 --- a/doc/device-tree-bindings/net/micrel-ksz90x1.txt +++ b/doc/device-tree-bindings/net/micrel-ksz90x1.txt @@ -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 diff --git a/drivers/net/ldpaa_eth/ldpaa_eth.c b/drivers/net/ldpaa_eth/ldpaa_eth.c index 73b7ba29df..34253e3924 100644 --- a/drivers/net/ldpaa_eth/ldpaa_eth.c +++ b/drivers/net/ldpaa_eth/ldpaa_eth.c @@ -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)); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3dc0822d9c..631b52b1cf 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -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" diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index 12df09877d..5c3298d612 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -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 { diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index 63e7b0242b..8dec9f2367 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -33,10 +33,14 @@ #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; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4e8d2943ee..c1c1af9abd 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -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; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index dd45e11b3a..8f1d759632 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -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, diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index 6db6edd0d0..6ac890a7f5 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -73,16 +73,6 @@ #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); } } diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 102fb91fff..957efb3984 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -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 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b55917bce1..90646ca55b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -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 index 0000000000..ad1a77fcfc --- /dev/null +++ b/drivers/phy/meson-g12a-usb2.c @@ -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 index 0000000000..920675dc99 --- /dev/null +++ b/drivers/phy/meson-g12a-usb3-pcie.c @@ -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, ®); + 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), +}; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index bbd8105c06..25e1a38aee 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -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 diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 60b5515a67..0b652a6f36 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -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 index 0000000000..832bcd70ff --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -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), + +}; diff --git a/include/phy.h b/include/phy.h index f23ca63f3b..d01435d1aa 100644 --- a/include/phy.h +++ b/include/phy.h @@ -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; |