1 /* 2 * Copyright (C) 2018 Ãlvaro Fernández Rojas <noltari@gmail.com> 3 * 4 * Derived from linux/arch/mips/bcm63xx/usb-common.c: 5 * Copyright 2008 Maxime Bizon <mbizon@freebox.fr> 6 * Copyright 2013 Florian Fainelli <florian@openwrt.org> 7 * 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 11 #include <common.h> 12 #include <clk.h> 13 #include <dm.h> 14 #include <generic-phy.h> 15 #include <power-domain.h> 16 #include <reset.h> 17 #include <asm/io.h> 18 #include <dm/device.h> 19 20 /* USBH PLL Control register */ 21 #define USBH_PLL_REG 0x18 22 #define USBH_PLL_IDDQ_PWRDN BIT(9) 23 #define USBH_PLL_PWRDN_DELAY BIT(10) 24 25 /* USBH Swap Control register */ 26 #define USBH_SWAP_REG 0x1c 27 #define USBH_SWAP_OHCI_DATA BIT(0) 28 #define USBH_SWAP_OHCI_ENDIAN BIT(1) 29 #define USBH_SWAP_EHCI_DATA BIT(3) 30 #define USBH_SWAP_EHCI_ENDIAN BIT(4) 31 32 /* USBH Setup register */ 33 #define USBH_SETUP_REG 0x28 34 #define USBH_SETUP_IOC BIT(4) 35 #define USBH_SETUP_IPP BIT(5) 36 37 struct bcm6368_usbh_hw { 38 uint32_t setup_clr; 39 uint32_t pll_clr; 40 }; 41 42 struct bcm6368_usbh_priv { 43 const struct bcm6368_usbh_hw *hw; 44 void __iomem *regs; 45 }; 46 47 static int bcm6368_usbh_init(struct phy *phy) 48 { 49 struct bcm6368_usbh_priv *priv = dev_get_priv(phy->dev); 50 const struct bcm6368_usbh_hw *hw = priv->hw; 51 52 /* configure to work in native cpu endian */ 53 clrsetbits_be32(priv->regs + USBH_SWAP_REG, 54 USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN, 55 USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA); 56 57 /* setup config */ 58 if (hw->setup_clr) 59 clrbits_be32(priv->regs + USBH_SETUP_REG, hw->setup_clr); 60 61 setbits_be32(priv->regs + USBH_SETUP_REG, USBH_SETUP_IOC); 62 63 /* enable pll control */ 64 if (hw->pll_clr) 65 clrbits_be32(priv->regs + USBH_PLL_REG, hw->pll_clr); 66 67 return 0; 68 } 69 70 static struct phy_ops bcm6368_usbh_ops = { 71 .init = bcm6368_usbh_init, 72 }; 73 74 static const struct bcm6368_usbh_hw bcm6328_hw = { 75 .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY, 76 .setup_clr = 0, 77 }; 78 79 static const struct bcm6368_usbh_hw bcm6362_hw = { 80 .pll_clr = 0, 81 .setup_clr = 0, 82 }; 83 84 static const struct bcm6368_usbh_hw bcm6368_hw = { 85 .pll_clr = 0, 86 .setup_clr = 0, 87 }; 88 89 static const struct bcm6368_usbh_hw bcm63268_hw = { 90 .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY, 91 .setup_clr = USBH_SETUP_IPP, 92 }; 93 94 static const struct udevice_id bcm6368_usbh_ids[] = { 95 { 96 .compatible = "brcm,bcm6328-usbh", 97 .data = (ulong)&bcm6328_hw, 98 }, { 99 .compatible = "brcm,bcm6362-usbh", 100 .data = (ulong)&bcm6362_hw, 101 }, { 102 .compatible = "brcm,bcm6368-usbh", 103 .data = (ulong)&bcm6368_hw, 104 }, { 105 .compatible = "brcm,bcm63268-usbh", 106 .data = (ulong)&bcm63268_hw, 107 }, { /* sentinel */ } 108 }; 109 110 static int bcm6368_usbh_probe(struct udevice *dev) 111 { 112 struct bcm6368_usbh_priv *priv = dev_get_priv(dev); 113 const struct bcm6368_usbh_hw *hw = 114 (const struct bcm6368_usbh_hw *)dev_get_driver_data(dev); 115 #if defined(CONFIG_POWER_DOMAIN) 116 struct power_domain pwr_dom; 117 #endif 118 struct reset_ctl rst_ctl; 119 struct clk clk; 120 fdt_addr_t addr; 121 fdt_size_t size; 122 int ret; 123 124 addr = devfdt_get_addr_size_index(dev, 0, &size); 125 if (addr == FDT_ADDR_T_NONE) 126 return -EINVAL; 127 128 priv->regs = ioremap(addr, size); 129 priv->hw = hw; 130 131 /* enable usbh clock */ 132 ret = clk_get_by_name(dev, "usbh", &clk); 133 if (ret < 0) 134 return ret; 135 136 ret = clk_enable(&clk); 137 if (ret < 0) 138 return ret; 139 140 ret = clk_free(&clk); 141 if (ret < 0) 142 return ret; 143 144 #if defined(CONFIG_POWER_DOMAIN) 145 /* enable power domain */ 146 ret = power_domain_get(dev, &pwr_dom); 147 if (ret < 0) 148 return ret; 149 150 ret = power_domain_on(&pwr_dom); 151 if (ret < 0) 152 return ret; 153 154 ret = power_domain_free(&pwr_dom); 155 if (ret < 0) 156 return ret; 157 #endif 158 159 /* perform reset */ 160 ret = reset_get_by_index(dev, 0, &rst_ctl); 161 if (ret < 0) 162 return ret; 163 164 ret = reset_deassert(&rst_ctl); 165 if (ret < 0) 166 return ret; 167 168 ret = reset_free(&rst_ctl); 169 if (ret < 0) 170 return ret; 171 172 /* enable usb_ref clock */ 173 ret = clk_get_by_name(dev, "usb_ref", &clk); 174 if (!ret) { 175 ret = clk_enable(&clk); 176 if (ret < 0) 177 return ret; 178 179 ret = clk_free(&clk); 180 if (ret < 0) 181 return ret; 182 } 183 184 mdelay(100); 185 186 return 0; 187 } 188 189 U_BOOT_DRIVER(bcm6368_usbh) = { 190 .name = "bcm6368-usbh", 191 .id = UCLASS_PHY, 192 .of_match = bcm6368_usbh_ids, 193 .ops = &bcm6368_usbh_ops, 194 .priv_auto_alloc_size = sizeof(struct bcm6368_usbh_priv), 195 .probe = bcm6368_usbh_probe, 196 }; 197