mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-30 08:03:32 +08:00
c9f8947e66
USB devices are not really designed to get the power bounced off and on at them. Esp. USB powered harddisks do not like this. Currently we power off the USB ports both on a "usb reset" and when booting the kernel, causing the usb-power to bounce off and then back on again. This patch removes the powering off calls, fixing the undesirable power bouncing. Note this requires some special handling for the OTG port: 1) We must skip the external vbus check if we've already enabled our own vbus to avoid false positives 2) If on an usb reset we no longer detect that the id-pin is grounded, turn off vbus as that means an external vbus may be present now Signed-off-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Ian Campbell <ijc@hellion.org.uk>
104 lines
2.8 KiB
C
104 lines
2.8 KiB
C
/*
|
|
* Sunxi ohci glue
|
|
*
|
|
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
|
|
*
|
|
* Based on code from
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/arch/usb_phy.h>
|
|
#include <asm/io.h>
|
|
#include <dm.h>
|
|
#include <usb.h>
|
|
#include "ohci.h"
|
|
|
|
struct ohci_sunxi_priv {
|
|
ohci_t ohci;
|
|
int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
|
|
int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */
|
|
int phy_index; /* Index of the usb-phy attached to this hcd */
|
|
};
|
|
|
|
static int ohci_usb_probe(struct udevice *dev)
|
|
{
|
|
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
|
|
struct ohci_sunxi_priv *priv = dev_get_priv(dev);
|
|
struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev);
|
|
|
|
bus_priv->companion = true;
|
|
|
|
/*
|
|
* This should go away once we've moved to the driver model for
|
|
* clocks resp. phys.
|
|
*/
|
|
if (regs == (void *)(SUNXI_USB1_BASE + 0x400)) {
|
|
priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
|
|
priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK;
|
|
priv->phy_index = 1;
|
|
} else {
|
|
priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI1;
|
|
priv->usb_gate_mask = CCM_USB_CTRL_OHCI1_CLK;
|
|
priv->phy_index = 2;
|
|
}
|
|
|
|
setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
|
|
setbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask);
|
|
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
|
setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
|
|
#endif
|
|
|
|
sunxi_usb_phy_init(priv->phy_index);
|
|
sunxi_usb_phy_power_on(priv->phy_index);
|
|
|
|
return ohci_register(dev, regs);
|
|
}
|
|
|
|
static int ohci_usb_remove(struct udevice *dev)
|
|
{
|
|
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
struct ohci_sunxi_priv *priv = dev_get_priv(dev);
|
|
int ret;
|
|
|
|
ret = ohci_deregister(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
sunxi_usb_phy_exit(priv->phy_index);
|
|
|
|
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
|
clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
|
|
#endif
|
|
clrbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask);
|
|
clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id ohci_usb_ids[] = {
|
|
{ .compatible = "allwinner,sun4i-a10-ohci", },
|
|
{ .compatible = "allwinner,sun5i-a13-ohci", },
|
|
{ .compatible = "allwinner,sun6i-a31-ohci", },
|
|
{ .compatible = "allwinner,sun7i-a20-ohci", },
|
|
{ .compatible = "allwinner,sun8i-a23-ohci", },
|
|
{ .compatible = "allwinner,sun9i-a80-ohci", },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(usb_ohci) = {
|
|
.name = "ohci_sunxi",
|
|
.id = UCLASS_USB,
|
|
.of_match = ohci_usb_ids,
|
|
.probe = ohci_usb_probe,
|
|
.remove = ohci_usb_remove,
|
|
.ops = &ohci_usb_ops,
|
|
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
|
.priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv),
|
|
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
|
};
|