uboot/drivers/usb/host/ehci-msm.c
<<
>>
Prefs
   1/*
   2 * Qualcomm EHCI driver
   3 *
   4 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
   5 *
   6 * Based on Linux driver
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0+
   9 */
  10
  11#include <common.h>
  12#include <dm.h>
  13#include <errno.h>
  14#include <fdtdec.h>
  15#include <libfdt.h>
  16#include <usb.h>
  17#include <usb/ehci-ci.h>
  18#include <usb/ulpi.h>
  19#include <wait_bit.h>
  20#include <asm/gpio.h>
  21#include <asm/io.h>
  22#include <linux/compat.h>
  23#include "ehci.h"
  24
  25/* PHY viewport regs */
  26#define ULPI_MISC_A_READ         0x96
  27#define ULPI_MISC_A_SET          0x97
  28#define ULPI_MISC_A_CLEAR        0x98
  29#define ULPI_MISC_A_VBUSVLDEXTSEL    (1 << 1)
  30#define ULPI_MISC_A_VBUSVLDEXT       (1 << 0)
  31
  32#define GEN2_SESS_VLD_CTRL_EN (1 << 7)
  33
  34#define SESS_VLD_CTRL         (1 << 25)
  35
  36struct msm_ehci_priv {
  37        struct ehci_ctrl ctrl; /* Needed by EHCI */
  38        struct usb_ehci *ehci; /* Start of IP core*/
  39        struct ulpi_viewport ulpi_vp; /* ULPI Viewport */
  40};
  41
  42int __weak board_prepare_usb(enum usb_init_type type)
  43{
  44        return 0;
  45}
  46
  47static void setup_usb_phy(struct msm_ehci_priv *priv)
  48{
  49        /* Select and enable external configuration with USB PHY */
  50        ulpi_write(&priv->ulpi_vp, (u8 *)ULPI_MISC_A_SET,
  51                   ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
  52}
  53
  54static void reset_usb_phy(struct msm_ehci_priv *priv)
  55{
  56        /* Disable VBUS mimicing in the controller. */
  57        ulpi_write(&priv->ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,
  58                   ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
  59}
  60
  61
  62static int msm_init_after_reset(struct ehci_ctrl *dev)
  63{
  64        struct msm_ehci_priv *p = container_of(dev, struct msm_ehci_priv, ctrl);
  65        struct usb_ehci *ehci = p->ehci;
  66
  67        /* select ULPI phy */
  68        writel(PORT_PTS_ULPI, &ehci->portsc);
  69        setup_usb_phy(p);
  70
  71        /* Enable sess_vld */
  72        setbits_le32(&ehci->genconfig2, GEN2_SESS_VLD_CTRL_EN);
  73
  74        /* Enable external vbus configuration in the LINK */
  75        setbits_le32(&ehci->usbcmd, SESS_VLD_CTRL);
  76
  77        /* USB_OTG_HS_AHB_BURST */
  78        writel(0x0, &ehci->sbuscfg);
  79
  80        /* USB_OTG_HS_AHB_MODE: HPROT_MODE */
  81        /* Bus access related config. */
  82        writel(0x08, &ehci->sbusmode);
  83
  84        /* set mode to host controller */
  85        writel(CM_HOST, &ehci->usbmode);
  86
  87        return 0;
  88}
  89
  90static const struct ehci_ops msm_ehci_ops = {
  91        .init_after_reset = msm_init_after_reset
  92};
  93
  94static int ehci_usb_probe(struct udevice *dev)
  95{
  96        struct msm_ehci_priv *p = dev_get_priv(dev);
  97        struct usb_ehci *ehci = p->ehci;
  98        struct ehci_hccr *hccr;
  99        struct ehci_hcor *hcor;
 100        int ret;
 101
 102        hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength);
 103        hcor = (struct ehci_hcor *)((phys_addr_t)hccr +
 104                        HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
 105
 106        ret = board_prepare_usb(USB_INIT_HOST);
 107        if (ret < 0)
 108                return ret;
 109
 110        return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0, USB_INIT_HOST);
 111}
 112
 113static int ehci_usb_remove(struct udevice *dev)
 114{
 115        struct msm_ehci_priv *p = dev_get_priv(dev);
 116        struct usb_ehci *ehci = p->ehci;
 117        int ret;
 118
 119        ret = ehci_deregister(dev);
 120        if (ret)
 121                return ret;
 122
 123        /* Stop controller. */
 124        clrbits_le32(&ehci->usbcmd, CMD_RUN);
 125
 126        reset_usb_phy(p);
 127
 128        ret = board_prepare_usb(USB_INIT_DEVICE); /* Board specific hook */
 129        if (ret < 0)
 130                return ret;
 131
 132        /* Reset controller */
 133        setbits_le32(&ehci->usbcmd, CMD_RESET);
 134
 135        /* Wait for reset */
 136        if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30,
 137                         false)) {
 138                printf("Stuck on USB reset.\n");
 139                return -ETIMEDOUT;
 140        }
 141
 142        return 0;
 143}
 144
 145static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
 146{
 147        struct msm_ehci_priv *priv = dev_get_priv(dev);
 148
 149        priv->ulpi_vp.port_num = 0;
 150        priv->ehci = (void *)dev_get_addr(dev);
 151
 152        if (priv->ehci == (void *)FDT_ADDR_T_NONE)
 153                return -EINVAL;
 154
 155        /* Warning: this will not work if viewport address is > 64 bit due to
 156         * ULPI design.
 157         */
 158        priv->ulpi_vp.viewport_addr = (phys_addr_t)&priv->ehci->ulpi_viewpoint;
 159
 160        return 0;
 161}
 162
 163static const struct udevice_id ehci_usb_ids[] = {
 164        { .compatible = "qcom,ehci-host", },
 165        { }
 166};
 167
 168U_BOOT_DRIVER(usb_ehci) = {
 169        .name   = "ehci_msm",
 170        .id     = UCLASS_USB,
 171        .of_match = ehci_usb_ids,
 172        .ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
 173        .probe = ehci_usb_probe,
 174        .remove = ehci_usb_remove,
 175        .ops    = &ehci_usb_ops,
 176        .priv_auto_alloc_size = sizeof(struct msm_ehci_priv),
 177        .flags  = DM_FLAG_ALLOC_PRIV_DMA,
 178};
 179