diff options
author | Andre Przywara <andre.przywara@arm.com> | 2018-07-05 00:57:48 +0100 |
---|---|---|
committer | Marek Vasut <marex@denx.de> | 2018-07-05 11:25:50 +0200 |
commit | 0bc846a769d43810af9e4622473b85ffaead3dea (patch) | |
tree | 8d4d62f1eb006b83edc58370778b0d142d762d3d /drivers/usb | |
parent | ff5d5cc2331033c8a6987bb644827b52484160d9 (diff) |
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 <andre.przywara@arm.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ohci-sunxi.c | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c index 0ddbdbe460..bb3c2475df 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; |