uboot/drivers/usb/host/ehci-fsl.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc.
   3 *
   4 * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
   5 *
   6 * Author: Tor Krill tor@excito.com
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0+
   9 */
  10
  11#include <common.h>
  12#include <pci.h>
  13#include <usb.h>
  14#include <asm/io.h>
  15#include <usb/ehci-fsl.h>
  16#include <hwconfig.h>
  17#include <fsl_usb.h>
  18#include <fdt_support.h>
  19
  20#include "ehci.h"
  21
  22#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
  23#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
  24#endif
  25
  26static void set_txfifothresh(struct usb_ehci *, u32);
  27
  28/* Check USB PHY clock valid */
  29static int usb_phy_clk_valid(struct usb_ehci *ehci)
  30{
  31        if (!((in_be32(&ehci->control) & PHY_CLK_VALID) ||
  32                        in_be32(&ehci->prictrl))) {
  33                printf("USB PHY clock invalid!\n");
  34                return 0;
  35        } else {
  36                return 1;
  37        }
  38}
  39
  40/*
  41 * Create the appropriate control structures to manage
  42 * a new EHCI host controller.
  43 *
  44 * Excerpts from linux ehci fsl driver.
  45 */
  46int ehci_hcd_init(int index, enum usb_init_type init,
  47                struct ehci_hccr **hccr, struct ehci_hcor **hcor)
  48{
  49        struct usb_ehci *ehci = NULL;
  50        const char *phy_type = NULL;
  51        size_t len;
  52        char current_usb_controller[5];
  53#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
  54        char usb_phy[5];
  55
  56        usb_phy[0] = '\0';
  57#endif
  58        if (has_erratum_a007075()) {
  59                /*
  60                 * A 5ms delay is needed after applying soft-reset to the
  61                 * controller to let external ULPI phy come out of reset.
  62                 * This delay needs to be added before re-initializing
  63                 * the controller after soft-resetting completes
  64                 */
  65                mdelay(5);
  66        }
  67        memset(current_usb_controller, '\0', 5);
  68        snprintf(current_usb_controller, 4, "usb%d", index+1);
  69
  70        switch (index) {
  71        case 0:
  72                ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR;
  73                break;
  74        case 1:
  75                ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB2_ADDR;
  76                break;
  77        default:
  78                printf("ERROR: wrong controller index!!\n");
  79                break;
  80        };
  81
  82        *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
  83        *hcor = (struct ehci_hcor *)((uint32_t) *hccr +
  84                        HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
  85
  86        /* Set to Host mode */
  87        setbits_le32(&ehci->usbmode, CM_HOST);
  88
  89        out_be32(&ehci->snoop1, SNOOP_SIZE_2GB);
  90        out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB);
  91
  92        /* Init phy */
  93        if (hwconfig_sub(current_usb_controller, "phy_type"))
  94                phy_type = hwconfig_subarg(current_usb_controller,
  95                                "phy_type", &len);
  96        else
  97                phy_type = getenv("usb_phy_type");
  98
  99        if (!phy_type) {
 100#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
 101                /* if none specified assume internal UTMI */
 102                strcpy(usb_phy, "utmi");
 103                phy_type = usb_phy;
 104#else
 105                printf("WARNING: USB phy type not defined !!\n");
 106                return -1;
 107#endif
 108        }
 109
 110        if (!strncmp(phy_type, "utmi", 4)) {
 111#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY)
 112                clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
 113                                PHY_CLK_SEL_UTMI);
 114                clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
 115                                UTMI_PHY_EN);
 116                udelay(1000); /* delay required for PHY Clk to appear */
 117#endif
 118                out_le32(&(*hcor)->or_portsc[0], PORT_PTS_UTMI);
 119                clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
 120                                USB_EN);
 121        } else {
 122                clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK,
 123                                PHY_CLK_SEL_ULPI);
 124                clrsetbits_be32(&ehci->control, UTMI_PHY_EN |
 125                                CONTROL_REGISTER_W1C_MASK, USB_EN);
 126                udelay(1000); /* delay required for PHY Clk to appear */
 127                if (!usb_phy_clk_valid(ehci))
 128                        return -EINVAL;
 129                out_le32(&(*hcor)->or_portsc[0], PORT_PTS_ULPI);
 130        }
 131
 132        out_be32(&ehci->prictrl, 0x0000000c);
 133        out_be32(&ehci->age_cnt_limit, 0x00000040);
 134        out_be32(&ehci->sictrl, 0x00000001);
 135
 136        in_le32(&ehci->usbmode);
 137
 138        if (has_erratum_a007798())
 139                set_txfifothresh(ehci, TXFIFOTHRESH);
 140
 141        return 0;
 142}
 143
 144/*
 145 * Destroy the appropriate control structures corresponding
 146 * the the EHCI host controller.
 147 */
 148int ehci_hcd_stop(int index)
 149{
 150        return 0;
 151}
 152
 153/*
 154 * Setting the value of TXFIFO_THRESH field in TXFILLTUNING register
 155 * to counter DDR latencies in writing data into Tx buffer.
 156 * This prevents Tx buffer from getting underrun
 157 */
 158static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh)
 159{
 160        u32 cmd;
 161        cmd = ehci_readl(&ehci->txfilltuning);
 162        cmd &= ~TXFIFO_THRESH_MASK;
 163        cmd |= TXFIFO_THRESH(txfifo_thresh);
 164        ehci_writel(&ehci->txfilltuning, cmd);
 165}
 166
 167#if defined(CONFIG_HAS_FSL_DR_USB) || defined(CONFIG_HAS_FSL_MPH_USB)
 168static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode,
 169                                       const char *phy_type, int start_offset)
 170{
 171        const char *compat_dr = "fsl-usb2-dr";
 172        const char *compat_mph = "fsl-usb2-mph";
 173        const char *prop_mode = "dr_mode";
 174        const char *prop_type = "phy_type";
 175        const char *node_type = NULL;
 176        int node_offset;
 177        int err;
 178
 179        node_offset = fdt_node_offset_by_compatible(blob,
 180                                                    start_offset, compat_mph);
 181        if (node_offset < 0) {
 182                node_offset = fdt_node_offset_by_compatible(blob,
 183                                                            start_offset,
 184                                                            compat_dr);
 185                if (node_offset < 0) {
 186                        printf("WARNING: could not find compatible node: %s",
 187                               fdt_strerror(node_offset));
 188                        return -1;
 189                }
 190                node_type = compat_dr;
 191        } else {
 192                node_type = compat_mph;
 193        }
 194
 195        if (mode) {
 196                err = fdt_setprop(blob, node_offset, prop_mode, mode,
 197                                  strlen(mode) + 1);
 198                if (err < 0)
 199                        printf("WARNING: could not set %s for %s: %s.\n",
 200                               prop_mode, node_type, fdt_strerror(err));
 201        }
 202
 203        if (phy_type) {
 204                err = fdt_setprop(blob, node_offset, prop_type, phy_type,
 205                                  strlen(phy_type) + 1);
 206                if (err < 0)
 207                        printf("WARNING: could not set %s for %s: %s.\n",
 208                               prop_type, node_type, fdt_strerror(err));
 209        }
 210
 211        return node_offset;
 212}
 213
 214static const char *fdt_usb_get_node_type(void *blob, int start_offset,
 215                                         int *node_offset)
 216{
 217        const char *compat_dr = "fsl-usb2-dr";
 218        const char *compat_mph = "fsl-usb2-mph";
 219        const char *node_type = NULL;
 220
 221        *node_offset = fdt_node_offset_by_compatible(blob, start_offset,
 222                                                     compat_mph);
 223        if (*node_offset < 0) {
 224                *node_offset = fdt_node_offset_by_compatible(blob,
 225                                                             start_offset,
 226                                                             compat_dr);
 227                if (*node_offset < 0) {
 228                        printf("ERROR: could not find compatible node: %s\n",
 229                               fdt_strerror(*node_offset));
 230                } else {
 231                        node_type = compat_dr;
 232                }
 233        } else {
 234                node_type = compat_mph;
 235        }
 236
 237        return node_type;
 238}
 239
 240static int fdt_fixup_usb_erratum(void *blob, const char *prop_erratum,
 241                                 int start_offset)
 242{
 243        int node_offset, err;
 244        const char *node_type = NULL;
 245
 246        node_type = fdt_usb_get_node_type(blob, start_offset, &node_offset);
 247        if (!node_type)
 248                return -1;
 249
 250        err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0);
 251        if (err < 0) {
 252                printf("ERROR: could not set %s for %s: %s.\n",
 253                       prop_erratum, node_type, fdt_strerror(err));
 254        }
 255
 256        return node_offset;
 257}
 258
 259void fdt_fixup_dr_usb(void *blob, bd_t *bd)
 260{
 261        static const char * const modes[] = { "host", "peripheral", "otg" };
 262        static const char * const phys[] = { "ulpi", "utmi" };
 263        int usb_erratum_a006261_off = -1;
 264        int usb_erratum_a007075_off = -1;
 265        int usb_erratum_a007792_off = -1;
 266        int usb_mode_off = -1;
 267        int usb_phy_off = -1;
 268        char str[5];
 269        int i, j;
 270
 271        for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
 272                const char *dr_mode_type = NULL;
 273                const char *dr_phy_type = NULL;
 274                int mode_idx = -1, phy_idx = -1;
 275
 276                snprintf(str, 5, "%s%d", "usb", i);
 277                if (hwconfig(str)) {
 278                        for (j = 0; j < ARRAY_SIZE(modes); j++) {
 279                                if (hwconfig_subarg_cmp(str, "dr_mode",
 280                                                        modes[j])) {
 281                                        mode_idx = j;
 282                                        break;
 283                                }
 284                        }
 285
 286                        for (j = 0; j < ARRAY_SIZE(phys); j++) {
 287                                if (hwconfig_subarg_cmp(str, "phy_type",
 288                                                        phys[j])) {
 289                                        phy_idx = j;
 290                                        break;
 291                                }
 292                        }
 293
 294                        if (mode_idx < 0 && phy_idx < 0) {
 295                                printf("WARNING: invalid phy or mode\n");
 296                                return;
 297                        }
 298
 299                        if (mode_idx > -1)
 300                                dr_mode_type = modes[mode_idx];
 301
 302                        if (phy_idx > -1)
 303                                dr_phy_type = phys[phy_idx];
 304                }
 305
 306                usb_mode_off = fdt_fixup_usb_mode_phy_type(blob,
 307                                                           dr_mode_type, NULL,
 308                                                           usb_mode_off);
 309
 310                if (usb_mode_off < 0)
 311                        return;
 312
 313                usb_phy_off = fdt_fixup_usb_mode_phy_type(blob,
 314                                                          NULL, dr_phy_type,
 315                                                          usb_phy_off);
 316
 317                if (usb_phy_off < 0)
 318                        return;
 319
 320                if (has_erratum_a006261()) {
 321                        usb_erratum_a006261_off =  fdt_fixup_usb_erratum
 322                                                   (blob,
 323                                                    "fsl,usb-erratum-a006261",
 324                                                    usb_erratum_a006261_off);
 325                        if (usb_erratum_a006261_off < 0)
 326                                return;
 327                }
 328                if (has_erratum_a007075()) {
 329                        usb_erratum_a007075_off =  fdt_fixup_usb_erratum
 330                                                   (blob,
 331                                                    "fsl,usb-erratum-a007075",
 332                                                    usb_erratum_a007075_off);
 333                        if (usb_erratum_a007075_off < 0)
 334                                return;
 335                }
 336                if (has_erratum_a007792()) {
 337                        usb_erratum_a007792_off =  fdt_fixup_usb_erratum
 338                                                   (blob,
 339                                                    "fsl,usb-erratum-a007792",
 340                                                    usb_erratum_a007792_off);
 341                        if (usb_erratum_a007792_off < 0)
 342                                return;
 343                }
 344        }
 345}
 346#endif
 347