Message ID | 20200820053543.2801672-4-sr@denx.de |
---|---|
State | Superseded |
Delegated to: | Daniel Schwierzeck |
Headers | show |
Series | mips/usb: Add Octeon xHCI USB support | expand |
Hi Stefan, On Thu, Aug 20, 2020 at 1:35 PM Stefan Roese <sr@denx.de> wrote: > > This patch adds the glue layer for the MIPS Octeon SoCs. Its ported nits: It's > mainly from the Linux code. > > Signed-off-by: Stefan Roese <sr@denx.de> > Cc: Bin Meng <bmeng.cn@gmail.com> > Cc: Marek Vasut <marex@denx.de> > --- > > (no changes since v1) > > drivers/usb/host/Kconfig | 9 + > drivers/usb/host/Makefile | 1 + > drivers/usb/host/dwc3-octeon-glue.c | 396 ++++++++++++++++++++++++++++ > 3 files changed, 406 insertions(+) > create mode 100644 drivers/usb/host/dwc3-octeon-glue.c > > diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig > index 1c374a7bd8..d71c12d888 100644 > --- a/drivers/usb/host/Kconfig > +++ b/drivers/usb/host/Kconfig > @@ -46,6 +46,15 @@ config USB_XHCI_MVEBU > SoCs, which includes Armada8K, Armada3700 and other Armada > family SoCs. > > +config USB_XHCI_OCTEON > + bool "Support for Marvell Octeon family on-chip xHCI USB controller" > + depends on ARCH_OCTEON > + default y > + help > + Enables support for the on-chip xHCI controller on Marvell Octeon > + family SoCs. This is a driver for the dwc3 to provide the glue logic > + to configure the controller. > + > config USB_XHCI_PCI > bool "Support for PCI-based xHCI USB controller" > depends on DM_USB > diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile > index 29d4f87e38..a12e8f2702 100644 > --- a/drivers/usb/host/Makefile > +++ b/drivers/usb/host/Makefile > @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o > obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o > obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o > obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o > +obj-$(CONFIG_USB_XHCI_OCTEON) += dwc3-octeon-glue.o > > # designware > obj-$(CONFIG_USB_DWC2) += dwc2.o > diff --git a/drivers/usb/host/dwc3-octeon-glue.c b/drivers/usb/host/dwc3-octeon-glue.c > new file mode 100644 > index 0000000000..7b1ca7ee33 > --- /dev/null > +++ b/drivers/usb/host/dwc3-octeon-glue.c > @@ -0,0 +1,396 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Octeon family DWC3 specific glue layer > + * > + * Copyright (C) 2020 Stefan Roese <sr@denx.de> > + * > + * The lowlevel init code is based on the Linux driver octeon-usb.c by nits: low-level > + * David Daney <david.daney@cavium.com>, which is: > + * Copyright (C) 2010-2017 Cavium Networks > + */ > + > +#include <dm.h> > +#include <errno.h> > +#include <usb.h> > +#include <asm/io.h> > +#include <dm/lists.h> > +#include <dm/of_access.h> > +#include <linux/bitfield.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/usb/dwc3.h> > +#include <linux/usb/otg.h> > +#include <mach/octeon-model.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +#define CVMX_GPIO_BIT_CFGX(i) (0x0001070000000900ull + ((i) * 8)) > +#define CVMX_GPIO_XBIT_CFGX(i) (0x0001070000000900ull + \ > + ((i) & 31) * 8 - 8 * 16) > + > +#define GPIO_BIT_CFG_TX_OE BIT_ULL(0) > +#define GPIO_BIT_CFG_OUTPUT_SEL GENMASK_ULL(20, 16) > + > +#define UCTL_CTL_UCTL_RST BIT_ULL(0) > +#define UCTL_CTL_UAHC_RST BIT_ULL(1) > +#define UCTL_CTL_UPHY_RST BIT_ULL(2) > +#define UCTL_CTL_DRD_MODE BIT_ULL(3) > +#define UCTL_CTL_SCLK_EN BIT_ULL(4) > +#define UCTL_CTL_HS_POWER_EN BIT_ULL(12) > +#define UCTL_CTL_SS_POWER_EN BIT_ULL(14) > +#define UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24) > +#define UCTL_CTL_H_CLKDIV_RST BIT_ULL(28) > +#define UCTL_CTL_H_CLK_EN BIT_ULL(30) > +#define UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32) > +#define UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38) > +#define UCTL_CTL_REF_SSP_EN BIT_ULL(39) > +#define UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40) > +#define UCTL_CTL_SSC_EN BIT_ULL(59) > +#define UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60) > + > +#define UCTL_HOST_CFG 0xe0 > +#define UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN BIT_ULL(24) > +#define UCTL_HOST_CFG_PPC_EN BIT_ULL(25) > + > +#define UCTL_SHIM_CFG 0xe8 > +#define UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0) > +#define UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8) > + > +#define OCTEON_H_CLKDIV_SEL 8 > +#define OCTEON_MIN_H_CLK_RATE 150000000 > +#define OCTEON_MAX_H_CLK_RATE 300000000 > + > +static u8 clk_div[OCTEON_H_CLKDIV_SEL] = {1, 2, 4, 6, 8, 16, 24, 32}; > + > +static int dwc3_octeon_config_power(struct udevice *dev, void __iomem *base) > +{ > + u64 uctl_host_cfg; > + u64 gpio_bit; > + u32 gpio_pwr[3]; > + int gpio, len, power_active_low; > + const struct device_node *node = dev_np(dev); > + int index = ((u64)base >> 24) & 1; > + void __iomem *gpio_bit_cfg; > + > + if (of_find_property(node, "power", &len)) { > + if (len == 12) { > + dev_read_u32_array(dev, "power", gpio_pwr, 3); > + power_active_low = gpio_pwr[2] & 0x01; > + gpio = gpio_pwr[1]; > + } else if (len == 8) { > + dev_read_u32_array(dev, "power", gpio_pwr, 2); > + power_active_low = 0; > + gpio = gpio_pwr[1]; > + } else { > + printf("dwc3 controller clock init failure\n"); > + return -EINVAL; > + } > + > + gpio_bit_cfg = ioremap(CVMX_GPIO_BIT_CFGX(gpio), 0); > + > + if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || > + OCTEON_IS_MODEL(OCTEON_CNF75XX)) && gpio <= 31) { > + gpio_bit = ioread64(gpio_bit_cfg); > + gpio_bit |= GPIO_BIT_CFG_TX_OE; > + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; > + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, > + index == 0 ? 0x14 : 0x15); > + iowrite64(gpio_bit, gpio_bit_cfg); > + } else if (gpio <= 15) { > + gpio_bit = ioread64(gpio_bit_cfg); > + gpio_bit |= GPIO_BIT_CFG_TX_OE; > + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; > + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, > + index == 0 ? 0x14 : 0x19); > + iowrite64(gpio_bit, gpio_bit_cfg); > + } else { > + gpio_bit_cfg = ioremap(CVMX_GPIO_XBIT_CFGX(gpio), 0); > + > + gpio_bit = ioread64(gpio_bit_cfg); > + gpio_bit |= GPIO_BIT_CFG_TX_OE; > + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; > + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, > + index == 0 ? 0x14 : 0x19); > + iowrite64(gpio_bit, gpio_bit_cfg); > + } > + > + /* Enable XHCI power control and set if active high or low. */ > + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); > + uctl_host_cfg |= UCTL_HOST_CFG_PPC_EN; > + if (power_active_low) > + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; > + else > + uctl_host_cfg |= UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; > + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); > + > + /* Wait for power to stabilize */ > + mdelay(10); > + } else { > + /* Disable XHCI power control and set if active high. */ > + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); > + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_EN; > + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; > + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); > + dev_warn(dev, "dwc3 controller clock init failure.\n"); > + } > + > + return 0; > +} > + > +static int dwc3_octeon_clocks_start(struct udevice *dev, void __iomem *base) > +{ > + u64 uctl_ctl; > + int ref_clk_sel = 2; > + u64 div; > + u32 clock_rate; > + int mpll_mul; > + int i; > + u64 h_clk_rate; > + void __iomem *uctl_ctl_reg = base; > + const char *ss_clock_type; > + const char *hs_clock_type; > + > + i = dev_read_u32(dev, "refclk-frequency", &clock_rate); > + if (i) { > + printf("No UCTL \"refclk-frequency\"\n"); > + return -EINVAL; > + } > + > + ss_clock_type = dev_read_string(dev, "refclk-type-ss"); > + if (!ss_clock_type) { > + printf("No UCTL \"refclk-type-ss\"\n"); > + return -EINVAL; > + } > + > + hs_clock_type = dev_read_string(dev, "refclk-type-hs"); > + if (!hs_clock_type) { > + printf("No UCTL \"refclk-type-hs\"\n"); > + return -EINVAL; > + } > + > + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { > + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) { > + ref_clk_sel = 0; > + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { > + ref_clk_sel = 2; > + } else { > + printf("Invalid HS clock type %s, using pll_ref_clk\n", > + hs_clock_type); > + } > + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { > + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) { > + ref_clk_sel = 1; > + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { > + ref_clk_sel = 3; > + } else { > + printf("Invalid HS clock type %s, using pll_ref_clk\n", > + hs_clock_type); > + ref_clk_sel = 3; > + } > + } else { > + printf("Invalid SS clock type %s, using dlmc_ref_clk0\n", > + ss_clock_type); > + } > + > + if ((ref_clk_sel == 0 || ref_clk_sel == 1) && clock_rate != 100000000) { nits: use a macro for 100000000 > + printf("Invalid UCTL clock rate of %u, using 100000000\n", > + clock_rate); > + } > + > + /* > + * Step 1: Wait for all voltages to be stable...that surely > + * happened before starting the kernel. SKIP > + */ For Linux kernel this could be skipped, howerver U-Boot should set up these, no? > + > + /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ ditto > + > + /* Step 3: Assert all resets. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl |= UCTL_CTL_UCTL_RST | UCTL_CTL_UAHC_RST | UCTL_CTL_UPHY_RST; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 4a: Reset the clock dividers. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl |= UCTL_CTL_H_CLKDIV_RST; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 4b: Select controller clock frequency. */ > + for (div = ARRAY_SIZE(clk_div) - 1; div >= 0; div--) { > + h_clk_rate = gd->bus_clk / clk_div[div]; > + if (h_clk_rate <= OCTEON_MAX_H_CLK_RATE && > + h_clk_rate >= OCTEON_MIN_H_CLK_RATE) > + break; > + } > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_SEL; > + uctl_ctl |= FIELD_PREP(UCTL_CTL_H_CLKDIV_SEL, div); > + uctl_ctl |= UCTL_CTL_H_CLK_EN; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + uctl_ctl = ioread64(uctl_ctl_reg); > + if (div != FIELD_GET(UCTL_CTL_H_CLKDIV_SEL, uctl_ctl) || > + !(uctl_ctl & UCTL_CTL_H_CLK_EN)) { > + printf("dwc3 controller clock init failure\n"); > + return -EINVAL; > + } > + > + /* Step 4c: Deassert the controller clock divider reset. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_RST; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 5a: Reference clock configuration. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl &= ~UCTL_CTL_REF_CLK_SEL; > + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_SEL, ref_clk_sel); > + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; > + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x07); > + uctl_ctl &= ~UCTL_CTL_REF_CLK_DIV2; > + > + switch (clock_rate) { > + default: > + printf("Invalid ref_clk %u, using 100000000 instead\n", > + clock_rate); > + fallthrough; > + case 100000000: nits: please define some macros for these numbers: > + mpll_mul = 0x19; > + if (ref_clk_sel < 2) { > + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; > + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x27); > + } > + break; > + case 50000000: > + mpll_mul = 0x32; > + break; > + case 125000000: > + mpll_mul = 0x28; > + break; > + } > + uctl_ctl &= ~UCTL_CTL_MPLL_MULTIPLIER; > + uctl_ctl |= FIELD_PREP(UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); > + > + /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ > + uctl_ctl |= UCTL_CTL_SSC_EN; > + > + /* Step 5c: Enable SuperSpeed. */ > + uctl_ctl |= UCTL_CTL_REF_SSP_EN; > + > + /* Step 5d: Configure PHYs. SKIP */ > + > + /* Step 6a & 6b: Power up PHYs. */ > + uctl_ctl |= UCTL_CTL_HS_POWER_EN; > + uctl_ctl |= UCTL_CTL_SS_POWER_EN; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 7: Wait 10 controller-clock cycles to take effect. */ > + udelay(10); > + > + /* Step 8a: Deassert UCTL reset signal. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl &= ~UCTL_CTL_UCTL_RST; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 8b: Wait 10 controller-clock cycles. */ > + udelay(10); > + > + /* Step 8c: Setup power-power control. */ > + if (dwc3_octeon_config_power(dev, base)) { > + printf("Error configuring power\n"); > + return -EINVAL; > + } > + > + /* Step 8d: Deassert UAHC reset signal. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl &= ~UCTL_CTL_UAHC_RST; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 8e: Wait 10 controller-clock cycles. */ > + udelay(10); > + > + /* Step 9: Enable conditional coprocessor clock of UCTL. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl |= UCTL_CTL_SCLK_EN; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + /* Step 10: Set for host mode only. */ > + uctl_ctl = ioread64(uctl_ctl_reg); > + uctl_ctl &= ~UCTL_CTL_DRD_MODE; > + iowrite64(uctl_ctl, uctl_ctl_reg); > + > + return 0; > +} > + > +static void dwc3_octeon_set_endian_mode(void __iomem *base) > +{ > + u64 shim_cfg; > + > + shim_cfg = ioread64(base + UCTL_SHIM_CFG); > + shim_cfg &= ~UCTL_SHIM_CFG_CSR_ENDIAN_MODE; > + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); > + shim_cfg &= ~UCTL_SHIM_CFG_DMA_ENDIAN_MODE; > + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); > + iowrite64(shim_cfg, base + UCTL_SHIM_CFG); > +} > + > +static void dwc3_octeon_phy_reset(void __iomem *base) > +{ > + u64 uctl_ctl; > + > + uctl_ctl = ioread64(base); > + uctl_ctl &= ~UCTL_CTL_UPHY_RST; > + iowrite64(uctl_ctl, base); > +} > + > +static int octeon_dwc3_glue_probe(struct udevice *dev) > +{ > + void __iomem *base; > + > + base = dev_remap_addr(dev); > + if (IS_ERR(base)) > + return PTR_ERR(base); > + > + dwc3_octeon_clocks_start(dev, base); > + dwc3_octeon_set_endian_mode(base); > + dwc3_octeon_phy_reset(base); > + > + return 0; > +} > + > +static int octeon_dwc3_glue_bind(struct udevice *dev) > +{ > + ofnode node, dwc3_node; > + > + /* Find snps,dwc3 node from subnode */ > + dwc3_node = ofnode_null(); > + ofnode_for_each_subnode(node, dev->node) { > + if (ofnode_device_is_compatible(node, "snps,dwc3")) > + dwc3_node = node; > + } > + > + if (!ofnode_valid(dwc3_node)) { > + printf("Can't find dwc3 subnode for %s\n", dev->name); > + return -ENODEV; > + } > + > + return dm_scan_fdt_dev(dev); > +} > + > +static int octeon_dwc3_glue_remove(struct udevice *dev) > +{ > + return 0; > +} > + > +static const struct udevice_id octeon_dwc3_glue_ids[] = { > + { .compatible = "cavium,octeon-7130-usb-uctl" }, > + { } > +}; > + > +U_BOOT_DRIVER(dwc3_octeon_glue) = { > + .name = "dwc3_octeon_glue", > + .id = UCLASS_NOP, > + .of_match = octeon_dwc3_glue_ids, > + .probe = octeon_dwc3_glue_probe, > + .remove = octeon_dwc3_glue_remove, remove this function as it's empty > + .bind = octeon_dwc3_glue_bind, > + .flags = DM_FLAG_ALLOC_PRIV_DMA, > +}; > -- Regards, Bin
Hi Bin, On 20.08.20 11:07, Bin Meng wrote: > Hi Stefan, > > On Thu, Aug 20, 2020 at 1:35 PM Stefan Roese <sr@denx.de> wrote: >> >> This patch adds the glue layer for the MIPS Octeon SoCs. Its ported > > nits: It's Ok. >> mainly from the Linux code. >> >> Signed-off-by: Stefan Roese <sr@denx.de> >> Cc: Bin Meng <bmeng.cn@gmail.com> >> Cc: Marek Vasut <marex@denx.de> >> --- >> >> (no changes since v1) >> >> drivers/usb/host/Kconfig | 9 + >> drivers/usb/host/Makefile | 1 + >> drivers/usb/host/dwc3-octeon-glue.c | 396 ++++++++++++++++++++++++++++ >> 3 files changed, 406 insertions(+) >> create mode 100644 drivers/usb/host/dwc3-octeon-glue.c >> >> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig >> index 1c374a7bd8..d71c12d888 100644 >> --- a/drivers/usb/host/Kconfig >> +++ b/drivers/usb/host/Kconfig >> @@ -46,6 +46,15 @@ config USB_XHCI_MVEBU >> SoCs, which includes Armada8K, Armada3700 and other Armada >> family SoCs. >> >> +config USB_XHCI_OCTEON >> + bool "Support for Marvell Octeon family on-chip xHCI USB controller" >> + depends on ARCH_OCTEON >> + default y >> + help >> + Enables support for the on-chip xHCI controller on Marvell Octeon >> + family SoCs. This is a driver for the dwc3 to provide the glue logic >> + to configure the controller. >> + >> config USB_XHCI_PCI >> bool "Support for PCI-based xHCI USB controller" >> depends on DM_USB >> diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile >> index 29d4f87e38..a12e8f2702 100644 >> --- a/drivers/usb/host/Makefile >> +++ b/drivers/usb/host/Makefile >> @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o >> obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o >> obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o >> obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o >> +obj-$(CONFIG_USB_XHCI_OCTEON) += dwc3-octeon-glue.o >> >> # designware >> obj-$(CONFIG_USB_DWC2) += dwc2.o >> diff --git a/drivers/usb/host/dwc3-octeon-glue.c b/drivers/usb/host/dwc3-octeon-glue.c >> new file mode 100644 >> index 0000000000..7b1ca7ee33 >> --- /dev/null >> +++ b/drivers/usb/host/dwc3-octeon-glue.c >> @@ -0,0 +1,396 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Octeon family DWC3 specific glue layer >> + * >> + * Copyright (C) 2020 Stefan Roese <sr@denx.de> >> + * >> + * The lowlevel init code is based on the Linux driver octeon-usb.c by > > nits: low-level Ok. >> + * David Daney <david.daney@cavium.com>, which is: >> + * Copyright (C) 2010-2017 Cavium Networks >> + */ >> + >> +#include <dm.h> >> +#include <errno.h> >> +#include <usb.h> >> +#include <asm/io.h> >> +#include <dm/lists.h> >> +#include <dm/of_access.h> >> +#include <linux/bitfield.h> >> +#include <linux/delay.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/usb/dwc3.h> >> +#include <linux/usb/otg.h> >> +#include <mach/octeon-model.h> >> + >> +DECLARE_GLOBAL_DATA_PTR; >> + >> +#define CVMX_GPIO_BIT_CFGX(i) (0x0001070000000900ull + ((i) * 8)) >> +#define CVMX_GPIO_XBIT_CFGX(i) (0x0001070000000900ull + \ >> + ((i) & 31) * 8 - 8 * 16) >> + >> +#define GPIO_BIT_CFG_TX_OE BIT_ULL(0) >> +#define GPIO_BIT_CFG_OUTPUT_SEL GENMASK_ULL(20, 16) >> + >> +#define UCTL_CTL_UCTL_RST BIT_ULL(0) >> +#define UCTL_CTL_UAHC_RST BIT_ULL(1) >> +#define UCTL_CTL_UPHY_RST BIT_ULL(2) >> +#define UCTL_CTL_DRD_MODE BIT_ULL(3) >> +#define UCTL_CTL_SCLK_EN BIT_ULL(4) >> +#define UCTL_CTL_HS_POWER_EN BIT_ULL(12) >> +#define UCTL_CTL_SS_POWER_EN BIT_ULL(14) >> +#define UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24) >> +#define UCTL_CTL_H_CLKDIV_RST BIT_ULL(28) >> +#define UCTL_CTL_H_CLK_EN BIT_ULL(30) >> +#define UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32) >> +#define UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38) >> +#define UCTL_CTL_REF_SSP_EN BIT_ULL(39) >> +#define UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40) >> +#define UCTL_CTL_SSC_EN BIT_ULL(59) >> +#define UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60) >> + >> +#define UCTL_HOST_CFG 0xe0 >> +#define UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN BIT_ULL(24) >> +#define UCTL_HOST_CFG_PPC_EN BIT_ULL(25) >> + >> +#define UCTL_SHIM_CFG 0xe8 >> +#define UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0) >> +#define UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8) >> + >> +#define OCTEON_H_CLKDIV_SEL 8 >> +#define OCTEON_MIN_H_CLK_RATE 150000000 >> +#define OCTEON_MAX_H_CLK_RATE 300000000 >> + >> +static u8 clk_div[OCTEON_H_CLKDIV_SEL] = {1, 2, 4, 6, 8, 16, 24, 32}; >> + >> +static int dwc3_octeon_config_power(struct udevice *dev, void __iomem *base) >> +{ >> + u64 uctl_host_cfg; >> + u64 gpio_bit; >> + u32 gpio_pwr[3]; >> + int gpio, len, power_active_low; >> + const struct device_node *node = dev_np(dev); >> + int index = ((u64)base >> 24) & 1; >> + void __iomem *gpio_bit_cfg; >> + >> + if (of_find_property(node, "power", &len)) { >> + if (len == 12) { >> + dev_read_u32_array(dev, "power", gpio_pwr, 3); >> + power_active_low = gpio_pwr[2] & 0x01; >> + gpio = gpio_pwr[1]; >> + } else if (len == 8) { >> + dev_read_u32_array(dev, "power", gpio_pwr, 2); >> + power_active_low = 0; >> + gpio = gpio_pwr[1]; >> + } else { >> + printf("dwc3 controller clock init failure\n"); >> + return -EINVAL; >> + } >> + >> + gpio_bit_cfg = ioremap(CVMX_GPIO_BIT_CFGX(gpio), 0); >> + >> + if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || >> + OCTEON_IS_MODEL(OCTEON_CNF75XX)) && gpio <= 31) { >> + gpio_bit = ioread64(gpio_bit_cfg); >> + gpio_bit |= GPIO_BIT_CFG_TX_OE; >> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; >> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, >> + index == 0 ? 0x14 : 0x15); >> + iowrite64(gpio_bit, gpio_bit_cfg); >> + } else if (gpio <= 15) { >> + gpio_bit = ioread64(gpio_bit_cfg); >> + gpio_bit |= GPIO_BIT_CFG_TX_OE; >> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; >> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, >> + index == 0 ? 0x14 : 0x19); >> + iowrite64(gpio_bit, gpio_bit_cfg); >> + } else { >> + gpio_bit_cfg = ioremap(CVMX_GPIO_XBIT_CFGX(gpio), 0); >> + >> + gpio_bit = ioread64(gpio_bit_cfg); >> + gpio_bit |= GPIO_BIT_CFG_TX_OE; >> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; >> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, >> + index == 0 ? 0x14 : 0x19); >> + iowrite64(gpio_bit, gpio_bit_cfg); >> + } >> + >> + /* Enable XHCI power control and set if active high or low. */ >> + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); >> + uctl_host_cfg |= UCTL_HOST_CFG_PPC_EN; >> + if (power_active_low) >> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; >> + else >> + uctl_host_cfg |= UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; >> + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); >> + >> + /* Wait for power to stabilize */ >> + mdelay(10); >> + } else { >> + /* Disable XHCI power control and set if active high. */ >> + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); >> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_EN; >> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; >> + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); >> + dev_warn(dev, "dwc3 controller clock init failure.\n"); >> + } >> + >> + return 0; >> +} >> + >> +static int dwc3_octeon_clocks_start(struct udevice *dev, void __iomem *base) >> +{ >> + u64 uctl_ctl; >> + int ref_clk_sel = 2; >> + u64 div; >> + u32 clock_rate; >> + int mpll_mul; >> + int i; >> + u64 h_clk_rate; >> + void __iomem *uctl_ctl_reg = base; >> + const char *ss_clock_type; >> + const char *hs_clock_type; >> + >> + i = dev_read_u32(dev, "refclk-frequency", &clock_rate); >> + if (i) { >> + printf("No UCTL \"refclk-frequency\"\n"); >> + return -EINVAL; >> + } >> + >> + ss_clock_type = dev_read_string(dev, "refclk-type-ss"); >> + if (!ss_clock_type) { >> + printf("No UCTL \"refclk-type-ss\"\n"); >> + return -EINVAL; >> + } >> + >> + hs_clock_type = dev_read_string(dev, "refclk-type-hs"); >> + if (!hs_clock_type) { >> + printf("No UCTL \"refclk-type-hs\"\n"); >> + return -EINVAL; >> + } >> + >> + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { >> + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) { >> + ref_clk_sel = 0; >> + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { >> + ref_clk_sel = 2; >> + } else { >> + printf("Invalid HS clock type %s, using pll_ref_clk\n", >> + hs_clock_type); >> + } >> + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { >> + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) { >> + ref_clk_sel = 1; >> + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { >> + ref_clk_sel = 3; >> + } else { >> + printf("Invalid HS clock type %s, using pll_ref_clk\n", >> + hs_clock_type); >> + ref_clk_sel = 3; >> + } >> + } else { >> + printf("Invalid SS clock type %s, using dlmc_ref_clk0\n", >> + ss_clock_type); >> + } >> + >> + if ((ref_clk_sel == 0 || ref_clk_sel == 1) && clock_rate != 100000000) { > > nits: use a macro for 100000000 I don't see a gain by doing so. Macros for (non-magic) numbers? Is CLOCK_100MHZ easier to read than 100000000 ? >> + printf("Invalid UCTL clock rate of %u, using 100000000\n", >> + clock_rate); >> + } >> + >> + /* >> + * Step 1: Wait for all voltages to be stable...that surely >> + * happened before starting the kernel. SKIP >> + */ > > For Linux kernel this could be skipped, howerver U-Boot should set up these, no? I agree, that the comment does not match. But nevertheless, once this code is run, the board is up for at least 1 or more seconds, because of the time intensive DDR init. BTW: I double-checked the original 2012 U-Boot code, and here also no code is executed (similar skip comment). >> + >> + /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ > > ditto Same as above. >> + >> + /* Step 3: Assert all resets. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl |= UCTL_CTL_UCTL_RST | UCTL_CTL_UAHC_RST | UCTL_CTL_UPHY_RST; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 4a: Reset the clock dividers. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl |= UCTL_CTL_H_CLKDIV_RST; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 4b: Select controller clock frequency. */ >> + for (div = ARRAY_SIZE(clk_div) - 1; div >= 0; div--) { >> + h_clk_rate = gd->bus_clk / clk_div[div]; >> + if (h_clk_rate <= OCTEON_MAX_H_CLK_RATE && >> + h_clk_rate >= OCTEON_MIN_H_CLK_RATE) >> + break; >> + } >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_SEL; >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_H_CLKDIV_SEL, div); >> + uctl_ctl |= UCTL_CTL_H_CLK_EN; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + if (div != FIELD_GET(UCTL_CTL_H_CLKDIV_SEL, uctl_ctl) || >> + !(uctl_ctl & UCTL_CTL_H_CLK_EN)) { >> + printf("dwc3 controller clock init failure\n"); >> + return -EINVAL; >> + } >> + >> + /* Step 4c: Deassert the controller clock divider reset. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_RST; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 5a: Reference clock configuration. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_SEL; >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_SEL, ref_clk_sel); >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x07); >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_DIV2; >> + >> + switch (clock_rate) { >> + default: >> + printf("Invalid ref_clk %u, using 100000000 instead\n", >> + clock_rate); >> + fallthrough; >> + case 100000000: > > nits: please define some macros for these numbers: Again, I don't see how this would make the code easier to read. >> + mpll_mul = 0x19; >> + if (ref_clk_sel < 2) { >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x27); >> + } >> + break; >> + case 50000000: >> + mpll_mul = 0x32; >> + break; >> + case 125000000: >> + mpll_mul = 0x28; >> + break; >> + } >> + uctl_ctl &= ~UCTL_CTL_MPLL_MULTIPLIER; >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); >> + >> + /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ >> + uctl_ctl |= UCTL_CTL_SSC_EN; >> + >> + /* Step 5c: Enable SuperSpeed. */ >> + uctl_ctl |= UCTL_CTL_REF_SSP_EN; >> + >> + /* Step 5d: Configure PHYs. SKIP */ >> + >> + /* Step 6a & 6b: Power up PHYs. */ >> + uctl_ctl |= UCTL_CTL_HS_POWER_EN; >> + uctl_ctl |= UCTL_CTL_SS_POWER_EN; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 7: Wait 10 controller-clock cycles to take effect. */ >> + udelay(10); >> + >> + /* Step 8a: Deassert UCTL reset signal. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl &= ~UCTL_CTL_UCTL_RST; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 8b: Wait 10 controller-clock cycles. */ >> + udelay(10); >> + >> + /* Step 8c: Setup power-power control. */ >> + if (dwc3_octeon_config_power(dev, base)) { >> + printf("Error configuring power\n"); >> + return -EINVAL; >> + } >> + >> + /* Step 8d: Deassert UAHC reset signal. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl &= ~UCTL_CTL_UAHC_RST; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 8e: Wait 10 controller-clock cycles. */ >> + udelay(10); >> + >> + /* Step 9: Enable conditional coprocessor clock of UCTL. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl |= UCTL_CTL_SCLK_EN; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + /* Step 10: Set for host mode only. */ >> + uctl_ctl = ioread64(uctl_ctl_reg); >> + uctl_ctl &= ~UCTL_CTL_DRD_MODE; >> + iowrite64(uctl_ctl, uctl_ctl_reg); >> + >> + return 0; >> +} >> + >> +static void dwc3_octeon_set_endian_mode(void __iomem *base) >> +{ >> + u64 shim_cfg; >> + >> + shim_cfg = ioread64(base + UCTL_SHIM_CFG); >> + shim_cfg &= ~UCTL_SHIM_CFG_CSR_ENDIAN_MODE; >> + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); >> + shim_cfg &= ~UCTL_SHIM_CFG_DMA_ENDIAN_MODE; >> + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); >> + iowrite64(shim_cfg, base + UCTL_SHIM_CFG); >> +} >> + >> +static void dwc3_octeon_phy_reset(void __iomem *base) >> +{ >> + u64 uctl_ctl; >> + >> + uctl_ctl = ioread64(base); >> + uctl_ctl &= ~UCTL_CTL_UPHY_RST; >> + iowrite64(uctl_ctl, base); >> +} >> + >> +static int octeon_dwc3_glue_probe(struct udevice *dev) >> +{ >> + void __iomem *base; >> + >> + base = dev_remap_addr(dev); >> + if (IS_ERR(base)) >> + return PTR_ERR(base); >> + >> + dwc3_octeon_clocks_start(dev, base); >> + dwc3_octeon_set_endian_mode(base); >> + dwc3_octeon_phy_reset(base); >> + >> + return 0; >> +} >> + >> +static int octeon_dwc3_glue_bind(struct udevice *dev) >> +{ >> + ofnode node, dwc3_node; >> + >> + /* Find snps,dwc3 node from subnode */ >> + dwc3_node = ofnode_null(); >> + ofnode_for_each_subnode(node, dev->node) { >> + if (ofnode_device_is_compatible(node, "snps,dwc3")) >> + dwc3_node = node; >> + } >> + >> + if (!ofnode_valid(dwc3_node)) { >> + printf("Can't find dwc3 subnode for %s\n", dev->name); >> + return -ENODEV; >> + } >> + >> + return dm_scan_fdt_dev(dev); >> +} >> + >> +static int octeon_dwc3_glue_remove(struct udevice *dev) >> +{ >> + return 0; >> +} >> + >> +static const struct udevice_id octeon_dwc3_glue_ids[] = { >> + { .compatible = "cavium,octeon-7130-usb-uctl" }, >> + { } >> +}; >> + >> +U_BOOT_DRIVER(dwc3_octeon_glue) = { >> + .name = "dwc3_octeon_glue", >> + .id = UCLASS_NOP, >> + .of_match = octeon_dwc3_glue_ids, >> + .probe = octeon_dwc3_glue_probe, >> + .remove = octeon_dwc3_glue_remove, > > remove this function as it's empty Yes, thanks. Will remove in next version. Thanks, Stefan
Hi Stefan, On Thu, Aug 20, 2020 at 7:45 PM Stefan Roese <sr@denx.de> wrote: > > Hi Bin, > > On 20.08.20 11:07, Bin Meng wrote: > > Hi Stefan, > > > > On Thu, Aug 20, 2020 at 1:35 PM Stefan Roese <sr@denx.de> wrote: > >> > >> This patch adds the glue layer for the MIPS Octeon SoCs. Its ported > > > > nits: It's > > Ok. > > >> mainly from the Linux code. > >> > >> Signed-off-by: Stefan Roese <sr@denx.de> > >> Cc: Bin Meng <bmeng.cn@gmail.com> > >> Cc: Marek Vasut <marex@denx.de> > >> --- > >> > >> (no changes since v1) > >> > >> drivers/usb/host/Kconfig | 9 + > >> drivers/usb/host/Makefile | 1 + > >> drivers/usb/host/dwc3-octeon-glue.c | 396 ++++++++++++++++++++++++++++ > >> 3 files changed, 406 insertions(+) > >> create mode 100644 drivers/usb/host/dwc3-octeon-glue.c > >> > >> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig > >> index 1c374a7bd8..d71c12d888 100644 > >> --- a/drivers/usb/host/Kconfig > >> +++ b/drivers/usb/host/Kconfig > >> @@ -46,6 +46,15 @@ config USB_XHCI_MVEBU > >> SoCs, which includes Armada8K, Armada3700 and other Armada > >> family SoCs. > >> > >> +config USB_XHCI_OCTEON > >> + bool "Support for Marvell Octeon family on-chip xHCI USB controller" > >> + depends on ARCH_OCTEON > >> + default y > >> + help > >> + Enables support for the on-chip xHCI controller on Marvell Octeon > >> + family SoCs. This is a driver for the dwc3 to provide the glue logic > >> + to configure the controller. > >> + > >> config USB_XHCI_PCI > >> bool "Support for PCI-based xHCI USB controller" > >> depends on DM_USB > >> diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile > >> index 29d4f87e38..a12e8f2702 100644 > >> --- a/drivers/usb/host/Makefile > >> +++ b/drivers/usb/host/Makefile > >> @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o > >> obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o > >> obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o > >> obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o > >> +obj-$(CONFIG_USB_XHCI_OCTEON) += dwc3-octeon-glue.o > >> > >> # designware > >> obj-$(CONFIG_USB_DWC2) += dwc2.o > >> diff --git a/drivers/usb/host/dwc3-octeon-glue.c b/drivers/usb/host/dwc3-octeon-glue.c > >> new file mode 100644 > >> index 0000000000..7b1ca7ee33 > >> --- /dev/null > >> +++ b/drivers/usb/host/dwc3-octeon-glue.c > >> @@ -0,0 +1,396 @@ > >> +// SPDX-License-Identifier: GPL-2.0 > >> +/* > >> + * Octeon family DWC3 specific glue layer > >> + * > >> + * Copyright (C) 2020 Stefan Roese <sr@denx.de> > >> + * > >> + * The lowlevel init code is based on the Linux driver octeon-usb.c by > > > > nits: low-level > > Ok. > > >> + * David Daney <david.daney@cavium.com>, which is: > >> + * Copyright (C) 2010-2017 Cavium Networks > >> + */ > >> + > >> +#include <dm.h> > >> +#include <errno.h> > >> +#include <usb.h> > >> +#include <asm/io.h> > >> +#include <dm/lists.h> > >> +#include <dm/of_access.h> > >> +#include <linux/bitfield.h> > >> +#include <linux/delay.h> > >> +#include <linux/err.h> > >> +#include <linux/io.h> > >> +#include <linux/usb/dwc3.h> > >> +#include <linux/usb/otg.h> > >> +#include <mach/octeon-model.h> > >> + > >> +DECLARE_GLOBAL_DATA_PTR; > >> + > >> +#define CVMX_GPIO_BIT_CFGX(i) (0x0001070000000900ull + ((i) * 8)) > >> +#define CVMX_GPIO_XBIT_CFGX(i) (0x0001070000000900ull + \ > >> + ((i) & 31) * 8 - 8 * 16) > >> + > >> +#define GPIO_BIT_CFG_TX_OE BIT_ULL(0) > >> +#define GPIO_BIT_CFG_OUTPUT_SEL GENMASK_ULL(20, 16) > >> + > >> +#define UCTL_CTL_UCTL_RST BIT_ULL(0) > >> +#define UCTL_CTL_UAHC_RST BIT_ULL(1) > >> +#define UCTL_CTL_UPHY_RST BIT_ULL(2) > >> +#define UCTL_CTL_DRD_MODE BIT_ULL(3) > >> +#define UCTL_CTL_SCLK_EN BIT_ULL(4) > >> +#define UCTL_CTL_HS_POWER_EN BIT_ULL(12) > >> +#define UCTL_CTL_SS_POWER_EN BIT_ULL(14) > >> +#define UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24) > >> +#define UCTL_CTL_H_CLKDIV_RST BIT_ULL(28) > >> +#define UCTL_CTL_H_CLK_EN BIT_ULL(30) > >> +#define UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32) > >> +#define UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38) > >> +#define UCTL_CTL_REF_SSP_EN BIT_ULL(39) > >> +#define UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40) > >> +#define UCTL_CTL_SSC_EN BIT_ULL(59) > >> +#define UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60) > >> + > >> +#define UCTL_HOST_CFG 0xe0 > >> +#define UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN BIT_ULL(24) > >> +#define UCTL_HOST_CFG_PPC_EN BIT_ULL(25) > >> + > >> +#define UCTL_SHIM_CFG 0xe8 > >> +#define UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0) > >> +#define UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8) > >> + > >> +#define OCTEON_H_CLKDIV_SEL 8 > >> +#define OCTEON_MIN_H_CLK_RATE 150000000 > >> +#define OCTEON_MAX_H_CLK_RATE 300000000 > >> + > >> +static u8 clk_div[OCTEON_H_CLKDIV_SEL] = {1, 2, 4, 6, 8, 16, 24, 32}; > >> + > >> +static int dwc3_octeon_config_power(struct udevice *dev, void __iomem *base) > >> +{ > >> + u64 uctl_host_cfg; > >> + u64 gpio_bit; > >> + u32 gpio_pwr[3]; > >> + int gpio, len, power_active_low; > >> + const struct device_node *node = dev_np(dev); > >> + int index = ((u64)base >> 24) & 1; > >> + void __iomem *gpio_bit_cfg; > >> + > >> + if (of_find_property(node, "power", &len)) { > >> + if (len == 12) { > >> + dev_read_u32_array(dev, "power", gpio_pwr, 3); > >> + power_active_low = gpio_pwr[2] & 0x01; > >> + gpio = gpio_pwr[1]; > >> + } else if (len == 8) { > >> + dev_read_u32_array(dev, "power", gpio_pwr, 2); > >> + power_active_low = 0; > >> + gpio = gpio_pwr[1]; > >> + } else { > >> + printf("dwc3 controller clock init failure\n"); > >> + return -EINVAL; > >> + } > >> + > >> + gpio_bit_cfg = ioremap(CVMX_GPIO_BIT_CFGX(gpio), 0); > >> + > >> + if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || > >> + OCTEON_IS_MODEL(OCTEON_CNF75XX)) && gpio <= 31) { > >> + gpio_bit = ioread64(gpio_bit_cfg); > >> + gpio_bit |= GPIO_BIT_CFG_TX_OE; > >> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; > >> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, > >> + index == 0 ? 0x14 : 0x15); > >> + iowrite64(gpio_bit, gpio_bit_cfg); > >> + } else if (gpio <= 15) { > >> + gpio_bit = ioread64(gpio_bit_cfg); > >> + gpio_bit |= GPIO_BIT_CFG_TX_OE; > >> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; > >> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, > >> + index == 0 ? 0x14 : 0x19); > >> + iowrite64(gpio_bit, gpio_bit_cfg); > >> + } else { > >> + gpio_bit_cfg = ioremap(CVMX_GPIO_XBIT_CFGX(gpio), 0); > >> + > >> + gpio_bit = ioread64(gpio_bit_cfg); > >> + gpio_bit |= GPIO_BIT_CFG_TX_OE; > >> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; > >> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, > >> + index == 0 ? 0x14 : 0x19); > >> + iowrite64(gpio_bit, gpio_bit_cfg); > >> + } > >> + > >> + /* Enable XHCI power control and set if active high or low. */ > >> + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); > >> + uctl_host_cfg |= UCTL_HOST_CFG_PPC_EN; > >> + if (power_active_low) > >> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; > >> + else > >> + uctl_host_cfg |= UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; > >> + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); > >> + > >> + /* Wait for power to stabilize */ > >> + mdelay(10); > >> + } else { > >> + /* Disable XHCI power control and set if active high. */ > >> + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); > >> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_EN; > >> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; > >> + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); > >> + dev_warn(dev, "dwc3 controller clock init failure.\n"); > >> + } > >> + > >> + return 0; > >> +} > >> + > >> +static int dwc3_octeon_clocks_start(struct udevice *dev, void __iomem *base) > >> +{ > >> + u64 uctl_ctl; > >> + int ref_clk_sel = 2; > >> + u64 div; > >> + u32 clock_rate; > >> + int mpll_mul; > >> + int i; > >> + u64 h_clk_rate; > >> + void __iomem *uctl_ctl_reg = base; > >> + const char *ss_clock_type; > >> + const char *hs_clock_type; > >> + > >> + i = dev_read_u32(dev, "refclk-frequency", &clock_rate); > >> + if (i) { > >> + printf("No UCTL \"refclk-frequency\"\n"); > >> + return -EINVAL; > >> + } > >> + > >> + ss_clock_type = dev_read_string(dev, "refclk-type-ss"); > >> + if (!ss_clock_type) { > >> + printf("No UCTL \"refclk-type-ss\"\n"); > >> + return -EINVAL; > >> + } > >> + > >> + hs_clock_type = dev_read_string(dev, "refclk-type-hs"); > >> + if (!hs_clock_type) { > >> + printf("No UCTL \"refclk-type-hs\"\n"); > >> + return -EINVAL; > >> + } > >> + > >> + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { > >> + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) { > >> + ref_clk_sel = 0; > >> + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { > >> + ref_clk_sel = 2; > >> + } else { > >> + printf("Invalid HS clock type %s, using pll_ref_clk\n", > >> + hs_clock_type); > >> + } > >> + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { > >> + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) { > >> + ref_clk_sel = 1; > >> + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { > >> + ref_clk_sel = 3; > >> + } else { > >> + printf("Invalid HS clock type %s, using pll_ref_clk\n", > >> + hs_clock_type); > >> + ref_clk_sel = 3; > >> + } > >> + } else { > >> + printf("Invalid SS clock type %s, using dlmc_ref_clk0\n", > >> + ss_clock_type); > >> + } > >> + > >> + if ((ref_clk_sel == 0 || ref_clk_sel == 1) && clock_rate != 100000000) { > > > > nits: use a macro for 100000000 > > I don't see a gain by doing so. Macros for (non-magic) numbers? Is > CLOCK_100MHZ easier to read than 100000000 ? Yes, it's easier to read as CLOCK_100MHZ, otherwise I will need to count the number of 0s :) > > >> + printf("Invalid UCTL clock rate of %u, using 100000000\n", > >> + clock_rate); > >> + } > >> + > >> + /* > >> + * Step 1: Wait for all voltages to be stable...that surely > >> + * happened before starting the kernel. SKIP > >> + */ > > > > For Linux kernel this could be skipped, howerver U-Boot should set up these, no? > > I agree, that the comment does not match. But nevertheless, once this > code is run, the board is up for at least 1 or more seconds, because > of the time intensive DDR init. > > BTW: I double-checked the original 2012 U-Boot code, and here also no > code is executed (similar skip comment). > > >> + > >> + /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ > > > > ditto > > Same as above. > > >> + > >> + /* Step 3: Assert all resets. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl |= UCTL_CTL_UCTL_RST | UCTL_CTL_UAHC_RST | UCTL_CTL_UPHY_RST; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 4a: Reset the clock dividers. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl |= UCTL_CTL_H_CLKDIV_RST; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 4b: Select controller clock frequency. */ > >> + for (div = ARRAY_SIZE(clk_div) - 1; div >= 0; div--) { > >> + h_clk_rate = gd->bus_clk / clk_div[div]; > >> + if (h_clk_rate <= OCTEON_MAX_H_CLK_RATE && > >> + h_clk_rate >= OCTEON_MIN_H_CLK_RATE) > >> + break; > >> + } > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_SEL; > >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_H_CLKDIV_SEL, div); > >> + uctl_ctl |= UCTL_CTL_H_CLK_EN; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + if (div != FIELD_GET(UCTL_CTL_H_CLKDIV_SEL, uctl_ctl) || > >> + !(uctl_ctl & UCTL_CTL_H_CLK_EN)) { > >> + printf("dwc3 controller clock init failure\n"); > >> + return -EINVAL; > >> + } > >> + > >> + /* Step 4c: Deassert the controller clock divider reset. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_RST; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 5a: Reference clock configuration. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_SEL; > >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_SEL, ref_clk_sel); > >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; > >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x07); > >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_DIV2; > >> + > >> + switch (clock_rate) { > >> + default: > >> + printf("Invalid ref_clk %u, using 100000000 instead\n", > >> + clock_rate); > >> + fallthrough; > >> + case 100000000: > > > > nits: please define some macros for these numbers: > > Again, I don't see how this would make the code easier to read. > > >> + mpll_mul = 0x19; > >> + if (ref_clk_sel < 2) { > >> + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; > >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x27); > >> + } > >> + break; > >> + case 50000000: > >> + mpll_mul = 0x32; > >> + break; > >> + case 125000000: > >> + mpll_mul = 0x28; > >> + break; > >> + } > >> + uctl_ctl &= ~UCTL_CTL_MPLL_MULTIPLIER; > >> + uctl_ctl |= FIELD_PREP(UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); > >> + > >> + /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ > >> + uctl_ctl |= UCTL_CTL_SSC_EN; > >> + > >> + /* Step 5c: Enable SuperSpeed. */ > >> + uctl_ctl |= UCTL_CTL_REF_SSP_EN; > >> + > >> + /* Step 5d: Configure PHYs. SKIP */ > >> + > >> + /* Step 6a & 6b: Power up PHYs. */ > >> + uctl_ctl |= UCTL_CTL_HS_POWER_EN; > >> + uctl_ctl |= UCTL_CTL_SS_POWER_EN; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 7: Wait 10 controller-clock cycles to take effect. */ > >> + udelay(10); > >> + > >> + /* Step 8a: Deassert UCTL reset signal. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl &= ~UCTL_CTL_UCTL_RST; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 8b: Wait 10 controller-clock cycles. */ > >> + udelay(10); > >> + > >> + /* Step 8c: Setup power-power control. */ > >> + if (dwc3_octeon_config_power(dev, base)) { > >> + printf("Error configuring power\n"); > >> + return -EINVAL; > >> + } > >> + > >> + /* Step 8d: Deassert UAHC reset signal. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl &= ~UCTL_CTL_UAHC_RST; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 8e: Wait 10 controller-clock cycles. */ > >> + udelay(10); > >> + > >> + /* Step 9: Enable conditional coprocessor clock of UCTL. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl |= UCTL_CTL_SCLK_EN; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + /* Step 10: Set for host mode only. */ > >> + uctl_ctl = ioread64(uctl_ctl_reg); > >> + uctl_ctl &= ~UCTL_CTL_DRD_MODE; > >> + iowrite64(uctl_ctl, uctl_ctl_reg); > >> + > >> + return 0; > >> +} > >> + > >> +static void dwc3_octeon_set_endian_mode(void __iomem *base) > >> +{ > >> + u64 shim_cfg; > >> + > >> + shim_cfg = ioread64(base + UCTL_SHIM_CFG); > >> + shim_cfg &= ~UCTL_SHIM_CFG_CSR_ENDIAN_MODE; > >> + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); > >> + shim_cfg &= ~UCTL_SHIM_CFG_DMA_ENDIAN_MODE; > >> + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); > >> + iowrite64(shim_cfg, base + UCTL_SHIM_CFG); > >> +} > >> + > >> +static void dwc3_octeon_phy_reset(void __iomem *base) > >> +{ > >> + u64 uctl_ctl; > >> + > >> + uctl_ctl = ioread64(base); > >> + uctl_ctl &= ~UCTL_CTL_UPHY_RST; > >> + iowrite64(uctl_ctl, base); > >> +} > >> + > >> +static int octeon_dwc3_glue_probe(struct udevice *dev) > >> +{ > >> + void __iomem *base; > >> + > >> + base = dev_remap_addr(dev); > >> + if (IS_ERR(base)) > >> + return PTR_ERR(base); > >> + > >> + dwc3_octeon_clocks_start(dev, base); > >> + dwc3_octeon_set_endian_mode(base); > >> + dwc3_octeon_phy_reset(base); > >> + > >> + return 0; > >> +} > >> + > >> +static int octeon_dwc3_glue_bind(struct udevice *dev) > >> +{ > >> + ofnode node, dwc3_node; > >> + > >> + /* Find snps,dwc3 node from subnode */ > >> + dwc3_node = ofnode_null(); > >> + ofnode_for_each_subnode(node, dev->node) { > >> + if (ofnode_device_is_compatible(node, "snps,dwc3")) > >> + dwc3_node = node; > >> + } > >> + > >> + if (!ofnode_valid(dwc3_node)) { > >> + printf("Can't find dwc3 subnode for %s\n", dev->name); > >> + return -ENODEV; > >> + } > >> + > >> + return dm_scan_fdt_dev(dev); > >> +} > >> + > >> +static int octeon_dwc3_glue_remove(struct udevice *dev) > >> +{ > >> + return 0; > >> +} > >> + > >> +static const struct udevice_id octeon_dwc3_glue_ids[] = { > >> + { .compatible = "cavium,octeon-7130-usb-uctl" }, > >> + { } > >> +}; > >> + > >> +U_BOOT_DRIVER(dwc3_octeon_glue) = { > >> + .name = "dwc3_octeon_glue", > >> + .id = UCLASS_NOP, > >> + .of_match = octeon_dwc3_glue_ids, > >> + .probe = octeon_dwc3_glue_probe, > >> + .remove = octeon_dwc3_glue_remove, > > > > remove this function as it's empty > > Yes, thanks. Will remove in next version. > Regards, Bin
Hi Bin, On 20.08.20 15:24, Bin Meng wrote: > Hi Stefan, > > On Thu, Aug 20, 2020 at 7:45 PM Stefan Roese <sr@denx.de> wrote: >> >> Hi Bin, >> >> On 20.08.20 11:07, Bin Meng wrote: >>> Hi Stefan, >>> >>> On Thu, Aug 20, 2020 at 1:35 PM Stefan Roese <sr@denx.de> wrote: >>>> >>>> This patch adds the glue layer for the MIPS Octeon SoCs. Its ported >>> >>> nits: It's >> >> Ok. >> >>>> mainly from the Linux code. >>>> >>>> Signed-off-by: Stefan Roese <sr@denx.de> >>>> Cc: Bin Meng <bmeng.cn@gmail.com> >>>> Cc: Marek Vasut <marex@denx.de> >>>> --- >>>> >>>> (no changes since v1) >>>> >>>> drivers/usb/host/Kconfig | 9 + >>>> drivers/usb/host/Makefile | 1 + >>>> drivers/usb/host/dwc3-octeon-glue.c | 396 ++++++++++++++++++++++++++++ >>>> 3 files changed, 406 insertions(+) >>>> create mode 100644 drivers/usb/host/dwc3-octeon-glue.c >>>> >>>> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig >>>> index 1c374a7bd8..d71c12d888 100644 >>>> --- a/drivers/usb/host/Kconfig >>>> +++ b/drivers/usb/host/Kconfig >>>> @@ -46,6 +46,15 @@ config USB_XHCI_MVEBU >>>> SoCs, which includes Armada8K, Armada3700 and other Armada >>>> family SoCs. >>>> >>>> +config USB_XHCI_OCTEON >>>> + bool "Support for Marvell Octeon family on-chip xHCI USB controller" >>>> + depends on ARCH_OCTEON >>>> + default y >>>> + help >>>> + Enables support for the on-chip xHCI controller on Marvell Octeon >>>> + family SoCs. This is a driver for the dwc3 to provide the glue logic >>>> + to configure the controller. >>>> + >>>> config USB_XHCI_PCI >>>> bool "Support for PCI-based xHCI USB controller" >>>> depends on DM_USB >>>> diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile >>>> index 29d4f87e38..a12e8f2702 100644 >>>> --- a/drivers/usb/host/Makefile >>>> +++ b/drivers/usb/host/Makefile >>>> @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o >>>> obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o >>>> obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o >>>> obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o >>>> +obj-$(CONFIG_USB_XHCI_OCTEON) += dwc3-octeon-glue.o >>>> >>>> # designware >>>> obj-$(CONFIG_USB_DWC2) += dwc2.o >>>> diff --git a/drivers/usb/host/dwc3-octeon-glue.c b/drivers/usb/host/dwc3-octeon-glue.c >>>> new file mode 100644 >>>> index 0000000000..7b1ca7ee33 >>>> --- /dev/null >>>> +++ b/drivers/usb/host/dwc3-octeon-glue.c >>>> @@ -0,0 +1,396 @@ >>>> +// SPDX-License-Identifier: GPL-2.0 >>>> +/* >>>> + * Octeon family DWC3 specific glue layer >>>> + * >>>> + * Copyright (C) 2020 Stefan Roese <sr@denx.de> >>>> + * >>>> + * The lowlevel init code is based on the Linux driver octeon-usb.c by >>> >>> nits: low-level >> >> Ok. >> >>>> + * David Daney <david.daney@cavium.com>, which is: >>>> + * Copyright (C) 2010-2017 Cavium Networks >>>> + */ >>>> + >>>> +#include <dm.h> >>>> +#include <errno.h> >>>> +#include <usb.h> >>>> +#include <asm/io.h> >>>> +#include <dm/lists.h> >>>> +#include <dm/of_access.h> >>>> +#include <linux/bitfield.h> >>>> +#include <linux/delay.h> >>>> +#include <linux/err.h> >>>> +#include <linux/io.h> >>>> +#include <linux/usb/dwc3.h> >>>> +#include <linux/usb/otg.h> >>>> +#include <mach/octeon-model.h> >>>> + >>>> +DECLARE_GLOBAL_DATA_PTR; >>>> + >>>> +#define CVMX_GPIO_BIT_CFGX(i) (0x0001070000000900ull + ((i) * 8)) >>>> +#define CVMX_GPIO_XBIT_CFGX(i) (0x0001070000000900ull + \ >>>> + ((i) & 31) * 8 - 8 * 16) >>>> + >>>> +#define GPIO_BIT_CFG_TX_OE BIT_ULL(0) >>>> +#define GPIO_BIT_CFG_OUTPUT_SEL GENMASK_ULL(20, 16) >>>> + >>>> +#define UCTL_CTL_UCTL_RST BIT_ULL(0) >>>> +#define UCTL_CTL_UAHC_RST BIT_ULL(1) >>>> +#define UCTL_CTL_UPHY_RST BIT_ULL(2) >>>> +#define UCTL_CTL_DRD_MODE BIT_ULL(3) >>>> +#define UCTL_CTL_SCLK_EN BIT_ULL(4) >>>> +#define UCTL_CTL_HS_POWER_EN BIT_ULL(12) >>>> +#define UCTL_CTL_SS_POWER_EN BIT_ULL(14) >>>> +#define UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24) >>>> +#define UCTL_CTL_H_CLKDIV_RST BIT_ULL(28) >>>> +#define UCTL_CTL_H_CLK_EN BIT_ULL(30) >>>> +#define UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32) >>>> +#define UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38) >>>> +#define UCTL_CTL_REF_SSP_EN BIT_ULL(39) >>>> +#define UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40) >>>> +#define UCTL_CTL_SSC_EN BIT_ULL(59) >>>> +#define UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60) >>>> + >>>> +#define UCTL_HOST_CFG 0xe0 >>>> +#define UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN BIT_ULL(24) >>>> +#define UCTL_HOST_CFG_PPC_EN BIT_ULL(25) >>>> + >>>> +#define UCTL_SHIM_CFG 0xe8 >>>> +#define UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0) >>>> +#define UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8) >>>> + >>>> +#define OCTEON_H_CLKDIV_SEL 8 >>>> +#define OCTEON_MIN_H_CLK_RATE 150000000 >>>> +#define OCTEON_MAX_H_CLK_RATE 300000000 >>>> + >>>> +static u8 clk_div[OCTEON_H_CLKDIV_SEL] = {1, 2, 4, 6, 8, 16, 24, 32}; >>>> + >>>> +static int dwc3_octeon_config_power(struct udevice *dev, void __iomem *base) >>>> +{ >>>> + u64 uctl_host_cfg; >>>> + u64 gpio_bit; >>>> + u32 gpio_pwr[3]; >>>> + int gpio, len, power_active_low; >>>> + const struct device_node *node = dev_np(dev); >>>> + int index = ((u64)base >> 24) & 1; >>>> + void __iomem *gpio_bit_cfg; >>>> + >>>> + if (of_find_property(node, "power", &len)) { >>>> + if (len == 12) { >>>> + dev_read_u32_array(dev, "power", gpio_pwr, 3); >>>> + power_active_low = gpio_pwr[2] & 0x01; >>>> + gpio = gpio_pwr[1]; >>>> + } else if (len == 8) { >>>> + dev_read_u32_array(dev, "power", gpio_pwr, 2); >>>> + power_active_low = 0; >>>> + gpio = gpio_pwr[1]; >>>> + } else { >>>> + printf("dwc3 controller clock init failure\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + gpio_bit_cfg = ioremap(CVMX_GPIO_BIT_CFGX(gpio), 0); >>>> + >>>> + if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || >>>> + OCTEON_IS_MODEL(OCTEON_CNF75XX)) && gpio <= 31) { >>>> + gpio_bit = ioread64(gpio_bit_cfg); >>>> + gpio_bit |= GPIO_BIT_CFG_TX_OE; >>>> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; >>>> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, >>>> + index == 0 ? 0x14 : 0x15); >>>> + iowrite64(gpio_bit, gpio_bit_cfg); >>>> + } else if (gpio <= 15) { >>>> + gpio_bit = ioread64(gpio_bit_cfg); >>>> + gpio_bit |= GPIO_BIT_CFG_TX_OE; >>>> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; >>>> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, >>>> + index == 0 ? 0x14 : 0x19); >>>> + iowrite64(gpio_bit, gpio_bit_cfg); >>>> + } else { >>>> + gpio_bit_cfg = ioremap(CVMX_GPIO_XBIT_CFGX(gpio), 0); >>>> + >>>> + gpio_bit = ioread64(gpio_bit_cfg); >>>> + gpio_bit |= GPIO_BIT_CFG_TX_OE; >>>> + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; >>>> + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, >>>> + index == 0 ? 0x14 : 0x19); >>>> + iowrite64(gpio_bit, gpio_bit_cfg); >>>> + } >>>> + >>>> + /* Enable XHCI power control and set if active high or low. */ >>>> + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); >>>> + uctl_host_cfg |= UCTL_HOST_CFG_PPC_EN; >>>> + if (power_active_low) >>>> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; >>>> + else >>>> + uctl_host_cfg |= UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; >>>> + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); >>>> + >>>> + /* Wait for power to stabilize */ >>>> + mdelay(10); >>>> + } else { >>>> + /* Disable XHCI power control and set if active high. */ >>>> + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); >>>> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_EN; >>>> + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; >>>> + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); >>>> + dev_warn(dev, "dwc3 controller clock init failure.\n"); >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int dwc3_octeon_clocks_start(struct udevice *dev, void __iomem *base) >>>> +{ >>>> + u64 uctl_ctl; >>>> + int ref_clk_sel = 2; >>>> + u64 div; >>>> + u32 clock_rate; >>>> + int mpll_mul; >>>> + int i; >>>> + u64 h_clk_rate; >>>> + void __iomem *uctl_ctl_reg = base; >>>> + const char *ss_clock_type; >>>> + const char *hs_clock_type; >>>> + >>>> + i = dev_read_u32(dev, "refclk-frequency", &clock_rate); >>>> + if (i) { >>>> + printf("No UCTL \"refclk-frequency\"\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + ss_clock_type = dev_read_string(dev, "refclk-type-ss"); >>>> + if (!ss_clock_type) { >>>> + printf("No UCTL \"refclk-type-ss\"\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + hs_clock_type = dev_read_string(dev, "refclk-type-hs"); >>>> + if (!hs_clock_type) { >>>> + printf("No UCTL \"refclk-type-hs\"\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { >>>> + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) { >>>> + ref_clk_sel = 0; >>>> + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { >>>> + ref_clk_sel = 2; >>>> + } else { >>>> + printf("Invalid HS clock type %s, using pll_ref_clk\n", >>>> + hs_clock_type); >>>> + } >>>> + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { >>>> + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) { >>>> + ref_clk_sel = 1; >>>> + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { >>>> + ref_clk_sel = 3; >>>> + } else { >>>> + printf("Invalid HS clock type %s, using pll_ref_clk\n", >>>> + hs_clock_type); >>>> + ref_clk_sel = 3; >>>> + } >>>> + } else { >>>> + printf("Invalid SS clock type %s, using dlmc_ref_clk0\n", >>>> + ss_clock_type); >>>> + } >>>> + >>>> + if ((ref_clk_sel == 0 || ref_clk_sel == 1) && clock_rate != 100000000) { >>> >>> nits: use a macro for 100000000 >> >> I don't see a gain by doing so. Macros for (non-magic) numbers? Is >> CLOCK_100MHZ easier to read than 100000000 ? > > Yes, it's easier to read as CLOCK_100MHZ, otherwise I will need to > count the number of 0s :) Okay, understood. Next version will have some macros for the numbers. ;) Thanks, Stefan >> >>>> + printf("Invalid UCTL clock rate of %u, using 100000000\n", >>>> + clock_rate); >>>> + } >>>> + >>>> + /* >>>> + * Step 1: Wait for all voltages to be stable...that surely >>>> + * happened before starting the kernel. SKIP >>>> + */ >>> >>> For Linux kernel this could be skipped, howerver U-Boot should set up these, no? >> >> I agree, that the comment does not match. But nevertheless, once this >> code is run, the board is up for at least 1 or more seconds, because >> of the time intensive DDR init. >> >> BTW: I double-checked the original 2012 U-Boot code, and here also no >> code is executed (similar skip comment). >> >>>> + >>>> + /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ >>> >>> ditto >> >> Same as above. >> >>>> + >>>> + /* Step 3: Assert all resets. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl |= UCTL_CTL_UCTL_RST | UCTL_CTL_UAHC_RST | UCTL_CTL_UPHY_RST; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 4a: Reset the clock dividers. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl |= UCTL_CTL_H_CLKDIV_RST; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 4b: Select controller clock frequency. */ >>>> + for (div = ARRAY_SIZE(clk_div) - 1; div >= 0; div--) { >>>> + h_clk_rate = gd->bus_clk / clk_div[div]; >>>> + if (h_clk_rate <= OCTEON_MAX_H_CLK_RATE && >>>> + h_clk_rate >= OCTEON_MIN_H_CLK_RATE) >>>> + break; >>>> + } >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_SEL; >>>> + uctl_ctl |= FIELD_PREP(UCTL_CTL_H_CLKDIV_SEL, div); >>>> + uctl_ctl |= UCTL_CTL_H_CLK_EN; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + if (div != FIELD_GET(UCTL_CTL_H_CLKDIV_SEL, uctl_ctl) || >>>> + !(uctl_ctl & UCTL_CTL_H_CLK_EN)) { >>>> + printf("dwc3 controller clock init failure\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + /* Step 4c: Deassert the controller clock divider reset. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_RST; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 5a: Reference clock configuration. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl &= ~UCTL_CTL_REF_CLK_SEL; >>>> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_SEL, ref_clk_sel); >>>> + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; >>>> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x07); >>>> + uctl_ctl &= ~UCTL_CTL_REF_CLK_DIV2; >>>> + >>>> + switch (clock_rate) { >>>> + default: >>>> + printf("Invalid ref_clk %u, using 100000000 instead\n", >>>> + clock_rate); >>>> + fallthrough; >>>> + case 100000000: >>> >>> nits: please define some macros for these numbers: >> >> Again, I don't see how this would make the code easier to read. >> >>>> + mpll_mul = 0x19; >>>> + if (ref_clk_sel < 2) { >>>> + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; >>>> + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x27); >>>> + } >>>> + break; >>>> + case 50000000: >>>> + mpll_mul = 0x32; >>>> + break; >>>> + case 125000000: >>>> + mpll_mul = 0x28; >>>> + break; >>>> + } >>>> + uctl_ctl &= ~UCTL_CTL_MPLL_MULTIPLIER; >>>> + uctl_ctl |= FIELD_PREP(UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); >>>> + >>>> + /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ >>>> + uctl_ctl |= UCTL_CTL_SSC_EN; >>>> + >>>> + /* Step 5c: Enable SuperSpeed. */ >>>> + uctl_ctl |= UCTL_CTL_REF_SSP_EN; >>>> + >>>> + /* Step 5d: Configure PHYs. SKIP */ >>>> + >>>> + /* Step 6a & 6b: Power up PHYs. */ >>>> + uctl_ctl |= UCTL_CTL_HS_POWER_EN; >>>> + uctl_ctl |= UCTL_CTL_SS_POWER_EN; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 7: Wait 10 controller-clock cycles to take effect. */ >>>> + udelay(10); >>>> + >>>> + /* Step 8a: Deassert UCTL reset signal. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl &= ~UCTL_CTL_UCTL_RST; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 8b: Wait 10 controller-clock cycles. */ >>>> + udelay(10); >>>> + >>>> + /* Step 8c: Setup power-power control. */ >>>> + if (dwc3_octeon_config_power(dev, base)) { >>>> + printf("Error configuring power\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + /* Step 8d: Deassert UAHC reset signal. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl &= ~UCTL_CTL_UAHC_RST; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 8e: Wait 10 controller-clock cycles. */ >>>> + udelay(10); >>>> + >>>> + /* Step 9: Enable conditional coprocessor clock of UCTL. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl |= UCTL_CTL_SCLK_EN; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + /* Step 10: Set for host mode only. */ >>>> + uctl_ctl = ioread64(uctl_ctl_reg); >>>> + uctl_ctl &= ~UCTL_CTL_DRD_MODE; >>>> + iowrite64(uctl_ctl, uctl_ctl_reg); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static void dwc3_octeon_set_endian_mode(void __iomem *base) >>>> +{ >>>> + u64 shim_cfg; >>>> + >>>> + shim_cfg = ioread64(base + UCTL_SHIM_CFG); >>>> + shim_cfg &= ~UCTL_SHIM_CFG_CSR_ENDIAN_MODE; >>>> + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); >>>> + shim_cfg &= ~UCTL_SHIM_CFG_DMA_ENDIAN_MODE; >>>> + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); >>>> + iowrite64(shim_cfg, base + UCTL_SHIM_CFG); >>>> +} >>>> + >>>> +static void dwc3_octeon_phy_reset(void __iomem *base) >>>> +{ >>>> + u64 uctl_ctl; >>>> + >>>> + uctl_ctl = ioread64(base); >>>> + uctl_ctl &= ~UCTL_CTL_UPHY_RST; >>>> + iowrite64(uctl_ctl, base); >>>> +} >>>> + >>>> +static int octeon_dwc3_glue_probe(struct udevice *dev) >>>> +{ >>>> + void __iomem *base; >>>> + >>>> + base = dev_remap_addr(dev); >>>> + if (IS_ERR(base)) >>>> + return PTR_ERR(base); >>>> + >>>> + dwc3_octeon_clocks_start(dev, base); >>>> + dwc3_octeon_set_endian_mode(base); >>>> + dwc3_octeon_phy_reset(base); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int octeon_dwc3_glue_bind(struct udevice *dev) >>>> +{ >>>> + ofnode node, dwc3_node; >>>> + >>>> + /* Find snps,dwc3 node from subnode */ >>>> + dwc3_node = ofnode_null(); >>>> + ofnode_for_each_subnode(node, dev->node) { >>>> + if (ofnode_device_is_compatible(node, "snps,dwc3")) >>>> + dwc3_node = node; >>>> + } >>>> + >>>> + if (!ofnode_valid(dwc3_node)) { >>>> + printf("Can't find dwc3 subnode for %s\n", dev->name); >>>> + return -ENODEV; >>>> + } >>>> + >>>> + return dm_scan_fdt_dev(dev); >>>> +} >>>> + >>>> +static int octeon_dwc3_glue_remove(struct udevice *dev) >>>> +{ >>>> + return 0; >>>> +} >>>> + >>>> +static const struct udevice_id octeon_dwc3_glue_ids[] = { >>>> + { .compatible = "cavium,octeon-7130-usb-uctl" }, >>>> + { } >>>> +}; >>>> + >>>> +U_BOOT_DRIVER(dwc3_octeon_glue) = { >>>> + .name = "dwc3_octeon_glue", >>>> + .id = UCLASS_NOP, >>>> + .of_match = octeon_dwc3_glue_ids, >>>> + .probe = octeon_dwc3_glue_probe, >>>> + .remove = octeon_dwc3_glue_remove, >>> >>> remove this function as it's empty >> >> Yes, thanks. Will remove in next version. >> > > Regards, > Bin > Viele Grüße, Stefan
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1c374a7bd8..d71c12d888 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -46,6 +46,15 @@ config USB_XHCI_MVEBU SoCs, which includes Armada8K, Armada3700 and other Armada family SoCs. +config USB_XHCI_OCTEON + bool "Support for Marvell Octeon family on-chip xHCI USB controller" + depends on ARCH_OCTEON + default y + help + Enables support for the on-chip xHCI controller on Marvell Octeon + family SoCs. This is a driver for the dwc3 to provide the glue logic + to configure the controller. + config USB_XHCI_PCI bool "Support for PCI-based xHCI USB controller" depends on DM_USB diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 29d4f87e38..a12e8f2702 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o +obj-$(CONFIG_USB_XHCI_OCTEON) += dwc3-octeon-glue.o # designware obj-$(CONFIG_USB_DWC2) += dwc2.o diff --git a/drivers/usb/host/dwc3-octeon-glue.c b/drivers/usb/host/dwc3-octeon-glue.c new file mode 100644 index 0000000000..7b1ca7ee33 --- /dev/null +++ b/drivers/usb/host/dwc3-octeon-glue.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Octeon family DWC3 specific glue layer + * + * Copyright (C) 2020 Stefan Roese <sr@denx.de> + * + * The lowlevel init code is based on the Linux driver octeon-usb.c by + * David Daney <david.daney@cavium.com>, which is: + * Copyright (C) 2010-2017 Cavium Networks + */ + +#include <dm.h> +#include <errno.h> +#include <usb.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <dm/of_access.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/usb/dwc3.h> +#include <linux/usb/otg.h> +#include <mach/octeon-model.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define CVMX_GPIO_BIT_CFGX(i) (0x0001070000000900ull + ((i) * 8)) +#define CVMX_GPIO_XBIT_CFGX(i) (0x0001070000000900ull + \ + ((i) & 31) * 8 - 8 * 16) + +#define GPIO_BIT_CFG_TX_OE BIT_ULL(0) +#define GPIO_BIT_CFG_OUTPUT_SEL GENMASK_ULL(20, 16) + +#define UCTL_CTL_UCTL_RST BIT_ULL(0) +#define UCTL_CTL_UAHC_RST BIT_ULL(1) +#define UCTL_CTL_UPHY_RST BIT_ULL(2) +#define UCTL_CTL_DRD_MODE BIT_ULL(3) +#define UCTL_CTL_SCLK_EN BIT_ULL(4) +#define UCTL_CTL_HS_POWER_EN BIT_ULL(12) +#define UCTL_CTL_SS_POWER_EN BIT_ULL(14) +#define UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24) +#define UCTL_CTL_H_CLKDIV_RST BIT_ULL(28) +#define UCTL_CTL_H_CLK_EN BIT_ULL(30) +#define UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32) +#define UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38) +#define UCTL_CTL_REF_SSP_EN BIT_ULL(39) +#define UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40) +#define UCTL_CTL_SSC_EN BIT_ULL(59) +#define UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60) + +#define UCTL_HOST_CFG 0xe0 +#define UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN BIT_ULL(24) +#define UCTL_HOST_CFG_PPC_EN BIT_ULL(25) + +#define UCTL_SHIM_CFG 0xe8 +#define UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0) +#define UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8) + +#define OCTEON_H_CLKDIV_SEL 8 +#define OCTEON_MIN_H_CLK_RATE 150000000 +#define OCTEON_MAX_H_CLK_RATE 300000000 + +static u8 clk_div[OCTEON_H_CLKDIV_SEL] = {1, 2, 4, 6, 8, 16, 24, 32}; + +static int dwc3_octeon_config_power(struct udevice *dev, void __iomem *base) +{ + u64 uctl_host_cfg; + u64 gpio_bit; + u32 gpio_pwr[3]; + int gpio, len, power_active_low; + const struct device_node *node = dev_np(dev); + int index = ((u64)base >> 24) & 1; + void __iomem *gpio_bit_cfg; + + if (of_find_property(node, "power", &len)) { + if (len == 12) { + dev_read_u32_array(dev, "power", gpio_pwr, 3); + power_active_low = gpio_pwr[2] & 0x01; + gpio = gpio_pwr[1]; + } else if (len == 8) { + dev_read_u32_array(dev, "power", gpio_pwr, 2); + power_active_low = 0; + gpio = gpio_pwr[1]; + } else { + printf("dwc3 controller clock init failure\n"); + return -EINVAL; + } + + gpio_bit_cfg = ioremap(CVMX_GPIO_BIT_CFGX(gpio), 0); + + if ((OCTEON_IS_MODEL(OCTEON_CN73XX) || + OCTEON_IS_MODEL(OCTEON_CNF75XX)) && gpio <= 31) { + gpio_bit = ioread64(gpio_bit_cfg); + gpio_bit |= GPIO_BIT_CFG_TX_OE; + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, + index == 0 ? 0x14 : 0x15); + iowrite64(gpio_bit, gpio_bit_cfg); + } else if (gpio <= 15) { + gpio_bit = ioread64(gpio_bit_cfg); + gpio_bit |= GPIO_BIT_CFG_TX_OE; + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, + index == 0 ? 0x14 : 0x19); + iowrite64(gpio_bit, gpio_bit_cfg); + } else { + gpio_bit_cfg = ioremap(CVMX_GPIO_XBIT_CFGX(gpio), 0); + + gpio_bit = ioread64(gpio_bit_cfg); + gpio_bit |= GPIO_BIT_CFG_TX_OE; + gpio_bit &= ~GPIO_BIT_CFG_OUTPUT_SEL; + gpio_bit |= FIELD_PREP(GPIO_BIT_CFG_OUTPUT_SEL, + index == 0 ? 0x14 : 0x19); + iowrite64(gpio_bit, gpio_bit_cfg); + } + + /* Enable XHCI power control and set if active high or low. */ + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); + uctl_host_cfg |= UCTL_HOST_CFG_PPC_EN; + if (power_active_low) + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; + else + uctl_host_cfg |= UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); + + /* Wait for power to stabilize */ + mdelay(10); + } else { + /* Disable XHCI power control and set if active high. */ + uctl_host_cfg = ioread64(base + UCTL_HOST_CFG); + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_EN; + uctl_host_cfg &= ~UCTL_HOST_CFG_PPC_ACTIVE_HIGH_EN; + iowrite64(uctl_host_cfg, base + UCTL_HOST_CFG); + dev_warn(dev, "dwc3 controller clock init failure.\n"); + } + + return 0; +} + +static int dwc3_octeon_clocks_start(struct udevice *dev, void __iomem *base) +{ + u64 uctl_ctl; + int ref_clk_sel = 2; + u64 div; + u32 clock_rate; + int mpll_mul; + int i; + u64 h_clk_rate; + void __iomem *uctl_ctl_reg = base; + const char *ss_clock_type; + const char *hs_clock_type; + + i = dev_read_u32(dev, "refclk-frequency", &clock_rate); + if (i) { + printf("No UCTL \"refclk-frequency\"\n"); + return -EINVAL; + } + + ss_clock_type = dev_read_string(dev, "refclk-type-ss"); + if (!ss_clock_type) { + printf("No UCTL \"refclk-type-ss\"\n"); + return -EINVAL; + } + + hs_clock_type = dev_read_string(dev, "refclk-type-hs"); + if (!hs_clock_type) { + printf("No UCTL \"refclk-type-hs\"\n"); + return -EINVAL; + } + + if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) { + if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0) { + ref_clk_sel = 0; + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { + ref_clk_sel = 2; + } else { + printf("Invalid HS clock type %s, using pll_ref_clk\n", + hs_clock_type); + } + } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) { + if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) { + ref_clk_sel = 1; + } else if (strcmp(hs_clock_type, "pll_ref_clk") == 0) { + ref_clk_sel = 3; + } else { + printf("Invalid HS clock type %s, using pll_ref_clk\n", + hs_clock_type); + ref_clk_sel = 3; + } + } else { + printf("Invalid SS clock type %s, using dlmc_ref_clk0\n", + ss_clock_type); + } + + if ((ref_clk_sel == 0 || ref_clk_sel == 1) && clock_rate != 100000000) { + printf("Invalid UCTL clock rate of %u, using 100000000\n", + clock_rate); + } + + /* + * Step 1: Wait for all voltages to be stable...that surely + * happened before starting the kernel. SKIP + */ + + /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */ + + /* Step 3: Assert all resets. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl |= UCTL_CTL_UCTL_RST | UCTL_CTL_UAHC_RST | UCTL_CTL_UPHY_RST; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 4a: Reset the clock dividers. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl |= UCTL_CTL_H_CLKDIV_RST; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 4b: Select controller clock frequency. */ + for (div = ARRAY_SIZE(clk_div) - 1; div >= 0; div--) { + h_clk_rate = gd->bus_clk / clk_div[div]; + if (h_clk_rate <= OCTEON_MAX_H_CLK_RATE && + h_clk_rate >= OCTEON_MIN_H_CLK_RATE) + break; + } + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_SEL; + uctl_ctl |= FIELD_PREP(UCTL_CTL_H_CLKDIV_SEL, div); + uctl_ctl |= UCTL_CTL_H_CLK_EN; + iowrite64(uctl_ctl, uctl_ctl_reg); + uctl_ctl = ioread64(uctl_ctl_reg); + if (div != FIELD_GET(UCTL_CTL_H_CLKDIV_SEL, uctl_ctl) || + !(uctl_ctl & UCTL_CTL_H_CLK_EN)) { + printf("dwc3 controller clock init failure\n"); + return -EINVAL; + } + + /* Step 4c: Deassert the controller clock divider reset. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl &= ~UCTL_CTL_H_CLKDIV_RST; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 5a: Reference clock configuration. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl &= ~UCTL_CTL_REF_CLK_SEL; + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_SEL, ref_clk_sel); + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x07); + uctl_ctl &= ~UCTL_CTL_REF_CLK_DIV2; + + switch (clock_rate) { + default: + printf("Invalid ref_clk %u, using 100000000 instead\n", + clock_rate); + fallthrough; + case 100000000: + mpll_mul = 0x19; + if (ref_clk_sel < 2) { + uctl_ctl &= ~UCTL_CTL_REF_CLK_FSEL; + uctl_ctl |= FIELD_PREP(UCTL_CTL_REF_CLK_FSEL, 0x27); + } + break; + case 50000000: + mpll_mul = 0x32; + break; + case 125000000: + mpll_mul = 0x28; + break; + } + uctl_ctl &= ~UCTL_CTL_MPLL_MULTIPLIER; + uctl_ctl |= FIELD_PREP(UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); + + /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ + uctl_ctl |= UCTL_CTL_SSC_EN; + + /* Step 5c: Enable SuperSpeed. */ + uctl_ctl |= UCTL_CTL_REF_SSP_EN; + + /* Step 5d: Configure PHYs. SKIP */ + + /* Step 6a & 6b: Power up PHYs. */ + uctl_ctl |= UCTL_CTL_HS_POWER_EN; + uctl_ctl |= UCTL_CTL_SS_POWER_EN; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 7: Wait 10 controller-clock cycles to take effect. */ + udelay(10); + + /* Step 8a: Deassert UCTL reset signal. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl &= ~UCTL_CTL_UCTL_RST; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 8b: Wait 10 controller-clock cycles. */ + udelay(10); + + /* Step 8c: Setup power-power control. */ + if (dwc3_octeon_config_power(dev, base)) { + printf("Error configuring power\n"); + return -EINVAL; + } + + /* Step 8d: Deassert UAHC reset signal. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl &= ~UCTL_CTL_UAHC_RST; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 8e: Wait 10 controller-clock cycles. */ + udelay(10); + + /* Step 9: Enable conditional coprocessor clock of UCTL. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl |= UCTL_CTL_SCLK_EN; + iowrite64(uctl_ctl, uctl_ctl_reg); + + /* Step 10: Set for host mode only. */ + uctl_ctl = ioread64(uctl_ctl_reg); + uctl_ctl &= ~UCTL_CTL_DRD_MODE; + iowrite64(uctl_ctl, uctl_ctl_reg); + + return 0; +} + +static void dwc3_octeon_set_endian_mode(void __iomem *base) +{ + u64 shim_cfg; + + shim_cfg = ioread64(base + UCTL_SHIM_CFG); + shim_cfg &= ~UCTL_SHIM_CFG_CSR_ENDIAN_MODE; + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1); + shim_cfg &= ~UCTL_SHIM_CFG_DMA_ENDIAN_MODE; + shim_cfg |= FIELD_PREP(UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1); + iowrite64(shim_cfg, base + UCTL_SHIM_CFG); +} + +static void dwc3_octeon_phy_reset(void __iomem *base) +{ + u64 uctl_ctl; + + uctl_ctl = ioread64(base); + uctl_ctl &= ~UCTL_CTL_UPHY_RST; + iowrite64(uctl_ctl, base); +} + +static int octeon_dwc3_glue_probe(struct udevice *dev) +{ + void __iomem *base; + + base = dev_remap_addr(dev); + if (IS_ERR(base)) + return PTR_ERR(base); + + dwc3_octeon_clocks_start(dev, base); + dwc3_octeon_set_endian_mode(base); + dwc3_octeon_phy_reset(base); + + return 0; +} + +static int octeon_dwc3_glue_bind(struct udevice *dev) +{ + ofnode node, dwc3_node; + + /* Find snps,dwc3 node from subnode */ + dwc3_node = ofnode_null(); + ofnode_for_each_subnode(node, dev->node) { + if (ofnode_device_is_compatible(node, "snps,dwc3")) + dwc3_node = node; + } + + if (!ofnode_valid(dwc3_node)) { + printf("Can't find dwc3 subnode for %s\n", dev->name); + return -ENODEV; + } + + return dm_scan_fdt_dev(dev); +} + +static int octeon_dwc3_glue_remove(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id octeon_dwc3_glue_ids[] = { + { .compatible = "cavium,octeon-7130-usb-uctl" }, + { } +}; + +U_BOOT_DRIVER(dwc3_octeon_glue) = { + .name = "dwc3_octeon_glue", + .id = UCLASS_NOP, + .of_match = octeon_dwc3_glue_ids, + .probe = octeon_dwc3_glue_probe, + .remove = octeon_dwc3_glue_remove, + .bind = octeon_dwc3_glue_bind, + .flags = DM_FLAG_ALLOC_PRIV_DMA, +};
This patch adds the glue layer for the MIPS Octeon SoCs. Its ported mainly from the Linux code. Signed-off-by: Stefan Roese <sr@denx.de> Cc: Bin Meng <bmeng.cn@gmail.com> Cc: Marek Vasut <marex@denx.de> --- (no changes since v1) drivers/usb/host/Kconfig | 9 + drivers/usb/host/Makefile | 1 + drivers/usb/host/dwc3-octeon-glue.c | 396 ++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 drivers/usb/host/dwc3-octeon-glue.c