1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Sunxi ohci glue 4 * 5 * Copyright (C) 2015 Hans de Goede <hdegoede (at) redhat.com> 6 * 7 * Based on code from 8 * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 9 */ 10 11 #include <common.h> 12 #include <asm/arch/clock.h> 13 #include <asm/io.h> 14 #include <dm.h> 15 #include <usb.h> 16 #include "ohci.h" 17 #include <generic-phy.h> 18 19 #ifdef CONFIG_SUNXI_GEN_SUN4I 20 #define BASE_DIST 0x8000 21 #define AHB_CLK_DIST 2 22 #else 23 #define BASE_DIST 0x1000 24 #define AHB_CLK_DIST 1 25 #endif 26 27 #define SUN6I_AHB_RESET0_CFG_OFFSET 0x2c0 28 #define SUN9I_AHB_RESET0_CFG_OFFSET 0x5a0 29 30 struct ohci_sunxi_cfg { 31 bool has_reset; 32 u32 extra_ahb_gate_mask; 33 u32 extra_usb_gate_mask; 34 u32 reset0_cfg_offset; 35 }; 36 37 struct ohci_sunxi_priv { 38 ohci_t ohci; 39 struct sunxi_ccm_reg *ccm; 40 u32 *reset0_cfg; 41 int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ 42 int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */ 43 struct phy phy; 44 const struct ohci_sunxi_cfg *cfg; 45 }; 46 47 static fdt_addr_t last_ohci_addr = 0; 48 49 static int ohci_usb_probe(struct udevice *dev) 50 { 51 struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); 52 struct ohci_sunxi_priv *priv = dev_get_priv(dev); 53 struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev); 54 int extra_ahb_gate_mask = 0; 55 u8 reg_mask = 0; 56 int phys, ret; 57 58 if ((fdt_addr_t)regs > last_ohci_addr) 59 last_ohci_addr = (fdt_addr_t)regs; 60 61 priv->cfg = (const struct ohci_sunxi_cfg *)dev_get_driver_data(dev); 62 priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 63 if (IS_ERR(priv->ccm)) 64 return PTR_ERR(priv->ccm); 65 66 priv->reset0_cfg = (void *)priv->ccm + 67 priv->cfg->reset0_cfg_offset; 68 69 phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); 70 if (phys < 0) { 71 phys = 0; 72 goto no_phy; 73 } 74 75 ret = generic_phy_get_by_name(dev, "usb", &priv->phy); 76 if (ret) { 77 pr_err("failed to get %s usb PHY\n", dev->name); 78 return ret; 79 } 80 81 ret = generic_phy_init(&priv->phy); 82 if (ret) { 83 pr_err("failed to init %s USB PHY\n", dev->name); 84 return ret; 85 } 86 87 ret = generic_phy_power_on(&priv->phy); 88 if (ret) { 89 pr_err("failed to power on %s USB PHY\n", dev->name); 90 return ret; 91 } 92 93 no_phy: 94 bus_priv->companion = true; 95 96 /* 97 * This should go away once we've moved to the driver model for 98 * clocks resp. phys. 99 */ 100 reg_mask = ((uintptr_t)regs - (SUNXI_USB1_BASE + 0x400)) / BASE_DIST; 101 priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0; 102 extra_ahb_gate_mask = priv->cfg->extra_ahb_gate_mask; 103 priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK; 104 priv->ahb_gate_mask <<= reg_mask * AHB_CLK_DIST; 105 extra_ahb_gate_mask <<= reg_mask * AHB_CLK_DIST; 106 priv->usb_gate_mask <<= reg_mask; 107 108 setbits_le32(&priv->ccm->ahb_gate0, 109 priv->ahb_gate_mask | extra_ahb_gate_mask); 110 setbits_le32(&priv->ccm->usb_clk_cfg, 111 priv->usb_gate_mask | priv->cfg->extra_usb_gate_mask); 112 if (priv->cfg->has_reset) 113 setbits_le32(priv->reset0_cfg, 114 priv->ahb_gate_mask | extra_ahb_gate_mask); 115 116 return ohci_register(dev, regs); 117 } 118 119 static int ohci_usb_remove(struct udevice *dev) 120 { 121 struct ohci_sunxi_priv *priv = dev_get_priv(dev); 122 fdt_addr_t base_addr = devfdt_get_addr(dev); 123 int ret; 124 125 if (generic_phy_valid(&priv->phy)) { 126 ret = generic_phy_exit(&priv->phy); 127 if (ret) { 128 pr_err("failed to exit %s USB PHY\n", dev->name); 129 return ret; 130 } 131 } 132 133 ret = ohci_deregister(dev); 134 if (ret) 135 return ret; 136 137 if (priv->cfg->has_reset) 138 clrbits_le32(priv->reset0_cfg, priv->ahb_gate_mask); 139 /* 140 * On the A64 CLK_USB_OHCI0 is the parent of CLK_USB_OHCI1, so 141 * we have to wait with bringing down any clock until the last 142 * OHCI controller is removed. 143 */ 144 if (!priv->cfg->extra_usb_gate_mask || base_addr == last_ohci_addr) { 145 u32 usb_gate_mask = priv->usb_gate_mask; 146 147 usb_gate_mask |= priv->cfg->extra_usb_gate_mask; 148 clrbits_le32(&priv->ccm->usb_clk_cfg, usb_gate_mask); 149 } 150 151 clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask); 152 153 return 0; 154 } 155 156 static const struct ohci_sunxi_cfg sun4i_a10_cfg = { 157 .has_reset = false, 158 }; 159 160 static const struct ohci_sunxi_cfg sun6i_a31_cfg = { 161 .has_reset = true, 162 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET, 163 }; 164 165 static const struct ohci_sunxi_cfg sun8i_h3_cfg = { 166 .has_reset = true, 167 .extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, 168 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET, 169 }; 170 171 static const struct ohci_sunxi_cfg sun9i_a80_cfg = { 172 .has_reset = true, 173 .reset0_cfg_offset = SUN9I_AHB_RESET0_CFG_OFFSET, 174 }; 175 176 static const struct ohci_sunxi_cfg sun50i_a64_cfg = { 177 .has_reset = true, 178 .extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0, 179 .extra_usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK, 180 .reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET, 181 }; 182 183 static const struct udevice_id ohci_usb_ids[] = { 184 { 185 .compatible = "allwinner,sun4i-a10-ohci", 186 .data = (ulong)&sun4i_a10_cfg, 187 }, 188 { 189 .compatible = "allwinner,sun5i-a13-ohci", 190 .data = (ulong)&sun4i_a10_cfg, 191 }, 192 { 193 .compatible = "allwinner,sun6i-a31-ohci", 194 .data = (ulong)&sun6i_a31_cfg, 195 }, 196 { 197 .compatible = "allwinner,sun7i-a20-ohci", 198 .data = (ulong)&sun4i_a10_cfg, 199 }, 200 { 201 .compatible = "allwinner,sun8i-a23-ohci", 202 .data = (ulong)&sun6i_a31_cfg, 203 }, 204 { 205 .compatible = "allwinner,sun8i-a83t-ohci", 206 .data = (ulong)&sun6i_a31_cfg, 207 }, 208 { 209 .compatible = "allwinner,sun8i-h3-ohci", 210 .data = (ulong)&sun8i_h3_cfg, 211 }, 212 { 213 .compatible = "allwinner,sun9i-a80-ohci", 214 .data = (ulong)&sun9i_a80_cfg, 215 }, 216 { 217 .compatible = "allwinner,sun50i-a64-ohci", 218 .data = (ulong)&sun50i_a64_cfg, 219 }, 220 { /* sentinel */ } 221 }; 222 223 U_BOOT_DRIVER(usb_ohci) = { 224 .name = "ohci_sunxi", 225 .id = UCLASS_USB, 226 .of_match = ohci_usb_ids, 227 .probe = ohci_usb_probe, 228 .remove = ohci_usb_remove, 229 .ops = &ohci_usb_ops, 230 .platdata_auto_alloc_size = sizeof(struct usb_platdata), 231 .priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv), 232 .flags = DM_FLAG_ALLOC_PRIV_DMA, 233 }; 234