// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2019 Broadcom Ltd. */ #include #include #include #include #include #include #include "ehci.h" #include #if defined(CONFIG_BCMBCA_PMC) #include "pmc_drv.h" #endif #define MDIO_USB2 0 /* USB Host contorl regs */ typedef struct usb_ctrl{ uint32_t setup; #define USBH_IPP (1<<5) #define USBH_IOC (1<<4) #define USBH_STRAP_IPP_SEL (1<<25) #define USB2_OC_DISABLE_PORT0 (1<<28) #define USB2_OC_DISABLE_PORT1 (1<<29) #define USB3_OC_DISABLE_PORT0 (1<<30) #define USB3_OC_DISABLE_PORT1 (1<<31) uint32_t pll_ctl; uint32_t fladj_value; uint32_t bridge_ctl; #define USB_BRCTL_OHCI_MEM_REQ_DIS (1<<16) uint32_t spare1; uint32_t mdio; uint32_t mdio2; uint32_t test_port_control; uint32_t usb_simctl; #define USBH_OHCI_MEM_REQ_DIS (1<<1) uint32_t usb_testctl; uint32_t usb_testmon; uint32_t utmi_ctl_1; uint32_t utmi_ctl_2; uint32_t usb_pm; #define XHC_SOFT_RESETB (1<<22) #define USB_PWRDWN (1<<31) uint32_t usb_pm_status; uint32_t spare3; uint32_t pll_ldo_ctl; uint32_t pll_ldo_pllbias; uint32_t pll_afe_bg_cntl; uint32_t afe_usbio_tst; uint32_t pll_ndiv_frac; uint32_t tp_diag; uint32_t ahb_capture_fifo; uint32_t spare4; uint32_t usb30_ctl1; #define PHY3_PLL_SEQ_START (1<<4) uint32_t usb30_ctl2; uint32_t usb30_ctl3; uint32_t usb30_ctl4; uint32_t usb30_pctl; uint32_t usb30_ctl5; uint32_t spare5; uint32_t spare6; uint32_t spare7; uint32_t unsused1[3]; uint32_t usb_device_ctl1; uint32_t usb_device_ctl2; uint32_t unsused2[22]; uint32_t usb20_id; uint32_t usb30_id; uint32_t bdc_coreid; uint32_t usb_revid; } usb_ctrl; struct bcmbca_usb_ctrl { volatile struct usb_ctrl *regp; }; struct bcmbca_ehci { struct ehci_ctrl ctrl; }; static void usb_mdio_write(volatile uint32_t *mdio, uint32_t reg, uint32_t val, int mode) { uint32_t data; data = (reg << 16) | val | mode; *mdio = data; data |= (1 << 25); *mdio = data; mdelay(1); data &= ~(1 << 25); *mdio = data; } static void usb2_eye_fix(volatile struct usb_ctrl *regp) { /* Updating USB 2.0 PHY registers */ usb_mdio_write((void *)®p->mdio, 0x1f, 0x80a0, MDIO_USB2); usb_mdio_write((void *)®p->mdio, 0x0a, 0xc6a0, MDIO_USB2); } #if defined (CONFIG_BCM63138) static void bcm63138B0_manual_usb_ldo_start(volatile struct usb_ctrl *regp) { regp->pll_ctl &= ~(1 << 30); /*pll_resetb=0*/ regp->utmi_ctl_1 = 0; regp->pll_ldo_ctl = 4; /*ldo_ctl=core_rdy */ regp->pll_ctl |= ( 1 << 31); /*pll_iddq=1*/ mdelay(10); regp->pll_ctl &= ~( 1 << 31); /*pll_iddq=0*/ regp->pll_ldo_ctl |= 1; /*ldo_ctl.AFE_LDO_PWRDWNB=1*/ regp->pll_ldo_ctl |= 2; /*ldo_ctl.AFE_BG_PWRDWNB=1*/ mdelay(1); regp->utmi_ctl_1 = 0x00020002;/* utmi_resetb &ref_clk_sel=0; */ regp->pll_ctl |= ( 1 << 30); /*pll_resetb=1*/ mdelay(10); } #endif static int ehci_usb_probe (struct udevice *dev) { struct ehci_hccr *hccr; struct ehci_hcor *hcor; int ret; struct resource res; struct udevice *ctrl_dev; uclass_get_device_by_driver(UCLASS_NOP, DM_GET_DRIVER(ctrl_bcmbca_drv), &ctrl_dev); ret = dev_read_resource_byname (dev, "usb-ehci", &res); if (ret) { dev_err(dev, "can't get usb-ehci register for usb (ret=%d)\n", ret); return ret; } hccr = devm_ioremap (dev, res.start, resource_size(&res)); hcor = (struct ehci_hcor *)((uint64_t)hccr + HC_LENGTH(ehci_readl(&(hccr)->cr_capbase))); debug("dev %p usb %d hccr %p hcor %p\n", dev, dev->seq, hccr, hcor); ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); return 0; } static int ehci_usb_remove(struct udevice *dev) { int ret; struct udevice *next_devp = dev; struct udevice *ctrl_dev; debug ("remove bcmbca ehci dev %p seq %d\n", dev, dev->seq); ret = ehci_deregister(dev); if (ret) { printf("failed to deregister bcmbca ehci seq %d\n", dev->seq); return ret; } ret = uclass_find_next_device (&next_devp); if (!ret && !next_devp) { uclass_get_device_by_driver(UCLASS_NOP, DM_GET_DRIVER(ctrl_bcmbca_drv), &ctrl_dev); if (ctrl_dev) device_remove(ctrl_dev, DM_REMOVE_NORMAL); } return 0; } static const struct udevice_id ehci_usb_ids[] = { { .compatible = "brcm,bcmbca-ehci" }, { } }; U_BOOT_DRIVER(ehci_bcmbca) = { .name = "ehci-bcmbca", .id = UCLASS_USB, .of_match = ehci_usb_ids, .probe = ehci_usb_probe, .remove = ehci_usb_remove, .ops = &ehci_usb_ops, .priv_auto_alloc_size = sizeof(struct bcmbca_ehci), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; /* definition of the dummy driver for usb ctrl */ static int bcmbca_usb_ctrl_probe (struct udevice *dev) { int ret; uint32_t val; struct resource res; volatile struct usb_ctrl *regp; struct bcmbca_usb_ctrl *priv = dev_get_priv (dev); debug("bcmbca_usb_ctrl_probe called....\n"); //usb-ctrl drive probe function is expected to be called once #if defined(CONFIG_BCMBCA_PMC) // Power On USB, EHCI with index 0 has to be the first USB device #if defined(CONFIG_BCM47622) || defined(CONFIG_BCM6756) if (PowerOnDevice(PMB_ADDR_USB31_20)) // replace with pmc_usb call later on { printf("Failed to Power Up USB Host\n"); return -1; } #elif defined(CONFIG_BCM6846) || defined(CONFIG_BCM6878) if (PowerOnDevice(PMB_ADDR_USB20_2X)) // replace with pmc_usb call later on { printf("Failed to Power Up USB Host\n"); return -1; } #else if (PowerOnDevice(PMB_ADDR_USB30_2X)) // replace with pmc_usb call later on { printf("Failed to Power Up USB Host\n"); return -1; } #endif mdelay(1); #endif priv->regp = NULL; ret = dev_read_resource_byname (dev, "usb-ctrl", &res); if (ret) { dev_err(dev, "can't get usb-ctrl register (ret=%d)\n", ret); return ret; } regp = devm_ioremap (dev, res.start, resource_size(&res)); priv->regp = regp; debug("usb-ctrl reg %p\n", priv->regp); /* adjust over current & port power polarity */ regp->setup |= (USBH_IOC); if (dev_read_bool(dev, "pwrflt-bias-pull-up")) regp->setup &= ~(USBH_IOC); /*overide strap for IPP*/ val = regp->setup; val &= ~(USBH_STRAP_IPP_SEL); val |= (USBH_IPP); regp->setup = val; if (dev_read_bool(dev, "pwron-bias-pull-up")) regp->setup &= ~(USBH_IPP); #if defined (CONFIG_BCM63138) mdelay(300); bcm63138B0_manual_usb_ldo_start(regp); #endif /*enable USB PHYs*/ mdelay(1); regp->usb_pm &= ~(USB_PWRDWN); mdelay(300); regp->usb_pm &= ~XHC_SOFT_RESETB; /*adjust the default AFE settings for better eye diagrams */ usb2_eye_fix(regp); /*initialize EHCI & OHCI settings*/ /* no swap for data & desciptors */ regp->bridge_ctl &= ~(0xf); /*clear lower 4 bits */ /* reset host controllers for possible fake overcurrent indications */ val = regp->usb_pm; regp->usb_pm = 0; regp->usb_pm = val; mdelay(1); return 0; } static int bcmbca_usb_ctrl_remove(struct udevice *dev) { debug("bcmbca_usb_ctrl_remove called\n"); #if defined(CONFIG_BCMBCA_PMC) #if defined(CONFIG_BCM47622) || defined(CONFIG_BCM6756) if (PowerOffDevice(PMB_ADDR_USB31_20, 0)) // replace with pmc_usb call later on { printf("Failed to Power Down USB Host\n"); return -1; } #elif defined(CONFIG_BCM6846) || defined(CONFIG_BCM6878) if (PowerOffDevice(PMB_ADDR_USB20_2X, 0)) // replace with pmc_usb call later on { printf("Failed to Power Down USB Host\n"); return -1; } #else if (PowerOffDevice(PMB_ADDR_USB30_2X, 0)) // replace with pmc_usb call later on { printf("Failed to Power Down USB Host\n"); return -1; } #endif #endif return 0; } static const struct udevice_id bcmbca_usb_ctrl_ids[] = { { .compatible = "brcm,bcmbca-usb-ctrl" }, { } }; U_BOOT_DRIVER(ctrl_bcmbca_drv) = { .name = "bcmbca-usbctrl", .id = UCLASS_NOP, .of_match = bcmbca_usb_ctrl_ids, .probe = bcmbca_usb_ctrl_probe, .remove = bcmbca_usb_ctrl_remove, .priv_auto_alloc_size = sizeof(struct bcmbca_usb_ctrl), };