From 0bc846a769d43810af9e4622473b85ffaead3dea Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 5 Jul 2018 00:57:48 +0100 Subject: sunxi: A64: OHCI: prevent turning off shared USB clock On the A64 the clock for the first USB controller is actually the parent of the clock for the second controller, so turning them off in that order makes the system hang. Fix this by only turning off *both* clocks when the *last* OHCI controller is brought down. This covers the case when only one controller is used. Signed-off-by: Andre Przywara --- drivers/usb/host/ohci-sunxi.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c index 0ddbdbe4609..bb3c2475df9 100644 --- a/drivers/usb/host/ohci-sunxi.c +++ b/drivers/usb/host/ohci-sunxi.c @@ -44,6 +44,8 @@ struct ohci_sunxi_priv { const struct ohci_sunxi_cfg *cfg; }; +static fdt_addr_t last_ohci_addr = 0; + static int ohci_usb_probe(struct udevice *dev) { struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); @@ -53,6 +55,9 @@ static int ohci_usb_probe(struct udevice *dev) u8 reg_mask = 0; int phys, ret; + if ((fdt_addr_t)regs > last_ohci_addr) + last_ohci_addr = (fdt_addr_t)regs; + priv->cfg = (const struct ohci_sunxi_cfg *)dev_get_driver_data(dev); priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; if (IS_ERR(priv->ccm)) @@ -114,6 +119,7 @@ no_phy: static int ohci_usb_remove(struct udevice *dev) { struct ohci_sunxi_priv *priv = dev_get_priv(dev); + fdt_addr_t base_addr = devfdt_get_addr(dev); int ret; if (generic_phy_valid(&priv->phy)) { @@ -130,7 +136,18 @@ static int ohci_usb_remove(struct udevice *dev) if (priv->cfg->has_reset) clrbits_le32(priv->reset0_cfg, priv->ahb_gate_mask); - clrbits_le32(&priv->ccm->usb_clk_cfg, priv->usb_gate_mask); + /* + * On the A64 CLK_USB_OHCI0 is the parent of CLK_USB_OHCI1, so + * we have to wait with bringing down any clock until the last + * OHCI controller is removed. + */ + if (!priv->cfg->extra_usb_gate_mask || base_addr == last_ohci_addr) { + u32 usb_gate_mask = priv->usb_gate_mask; + + usb_gate_mask |= priv->cfg->extra_usb_gate_mask; + clrbits_le32(&priv->ccm->usb_clk_cfg, usb_gate_mask); + } + clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask); return 0; -- cgit v1.2.3