summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2018-07-05 00:57:48 +0100
committerMarek Vasut <marex@denx.de>2018-07-05 11:25:50 +0200
commit0bc846a769d43810af9e4622473b85ffaead3dea (patch)
tree8d4d62f1eb006b83edc58370778b0d142d762d3d /drivers/usb
parentff5d5cc2331033c8a6987bb644827b52484160d9 (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.c19
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;