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                return -EINVAL;
  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        if (has_erratum_a004477()) {
 142                /*
 143                 * When reset is issued while any ULPI transaction is ongoing
 144                 * then it may result to corruption of ULPI Function Control
 145                 * Register which eventually causes phy clock to enter low
 146                 * power mode which stops the clock. Thus delay is required
 147                 * before reset to let ongoing ULPI transaction complete.
 148                 */
 149                udelay(1);
 150        }
 151        return 0;
 152}
 153
 154/*
 155 * Destroy the appropriate control structures corresponding
 156 * the the EHCI host controller.
 157 */
 158int ehci_hcd_stop(int index)
 159{
 160        return 0;
 161}
 162
 163/*
 164 * Setting the value of TXFIFO_THRESH field in TXFILLTUNING register
 165 * to counter DDR latencies in writing data into Tx buffer.
 166 * This prevents Tx buffer from getting underrun
 167 */
 168static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh)
 169{
 170        u32 cmd;
 171        cmd = ehci_readl(&ehci->txfilltuning);
 172        cmd &= ~TXFIFO_THRESH_MASK;
 173        cmd |= TXFIFO_THRESH(txfifo_thresh);
 174        ehci_writel(&ehci->txfilltuning, cmd);
 175}
 176
 177#if defined(CONFIG_HAS_FSL_DR_USB) || defined(CONFIG_HAS_FSL_MPH_USB)
 178static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode,
 179                                       const char *phy_type, int start_offset)
 180{
 181        const char *compat_dr = "fsl-usb2-dr";
 182        const char *compat_mph = "fsl-usb2-mph";
 183        const char *prop_mode = "dr_mode";
 184        const char *prop_type = "phy_type";
 185        const char *node_type = NULL;
 186        int node_offset;
 187        int err;
 188
 189        node_offset = fdt_node_offset_by_compatible(blob,
 190                                                    start_offset, compat_mph);
 191        if (node_offset < 0) {
 192                node_offset = fdt_node_offset_by_compatible(blob,
 193                                                            start_offset,
 194                                                            compat_dr);
 195                if (node_offset < 0) {
 196                        printf("WARNING: could not find compatible node: %s",
 197                               fdt_strerror(node_offset));
 198                        return -1;
 199                }
 200                node_type = compat_dr;
 201        } else {
 202                node_type = compat_mph;
 203        }
 204
 205        if (mode) {
 206                err = fdt_setprop(blob, node_offset, prop_mode, mode,
 207                                  strlen(mode) + 1);
 208                if (err < 0)
 209                        printf("WARNING: could not set %s for %s: %s.\n",
 210                               prop_mode, node_type, fdt_strerror(err));
 211        }
 212
 213        if (phy_type) {
 214                err = fdt_setprop(blob, node_offset, prop_type, phy_type,
 215                                  strlen(phy_type) + 1);
 216                if (err < 0)
 217                        printf("WARNING: could not set %s for %s: %s.\n",
 218                               prop_type, node_type, fdt_strerror(err));
 219        }
 220
 221        return node_offset;
 222}
 223
 224static const char *fdt_usb_get_node_type(void *blob, int start_offset,
 225                                         int *node_offset)
 226{
 227        const char *compat_dr = "fsl-usb2-dr";
 228        const char *compat_mph = "fsl-usb2-mph";
 229        const char *node_type = NULL;
 230
 231        *node_offset = fdt_node_offset_by_compatible(blob, start_offset,
 232                                                     compat_mph);
 233        if (*node_offset < 0) {
 234                *node_offset = fdt_node_offset_by_compatible(blob,
 235                                                             start_offset,
 236                                                             compat_dr);
 237                if (*node_offset < 0) {
 238                        printf("ERROR: could not find compatible node: %s\n",
 239                               fdt_strerror(*node_offset));
 240                } else {
 241                        node_type = compat_dr;
 242                }
 243        } else {
 244                node_type = compat_mph;
 245        }
 246
 247        return node_type;
 248}
 249
 250static int fdt_fixup_usb_erratum(void *blob, const char *prop_erratum,
 251                                 int start_offset)
 252{
 253        int node_offset, err;
 254        const char *node_type = NULL;
 255
 256        node_type = fdt_usb_get_node_type(blob, start_offset, &node_offset);
 257        if (!node_type)
 258                return -1;
 259
 260        err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0);
 261        if (err < 0) {
 262                printf("ERROR: could not set %s for %s: %s.\n",
 263                       prop_erratum, node_type, fdt_strerror(err));
 264        }
 265
 266        return node_offset;
 267}
 268
 269void fdt_fixup_dr_usb(void *blob, bd_t *bd)
 270{
 271        static const char * const modes[] = { "host", "peripheral", "otg" };
 272        static const char * const phys[] = { "ulpi", "utmi", "utmi_dual" };
 273        int usb_erratum_a006261_off = -1;
 274        int usb_erratum_a007075_off = -1;
 275        int usb_erratum_a007792_off = -1;
 276        int usb_erratum_a005697_off = -1;
 277        int usb_mode_off = -1;
 278        int usb_phy_off = -1;
 279        char str[5];
 280        int i, j;
 281
 282        for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
 283                const char *dr_mode_type = NULL;
 284                const char *dr_phy_type = NULL;
 285                int mode_idx = -1, phy_idx = -1;
 286
 287                snprintf(str, 5, "%s%d", "usb", i);
 288                if (hwconfig(str)) {
 289                        for (j = 0; j < ARRAY_SIZE(modes); j++) {
 290                                if (hwconfig_subarg_cmp(str, "dr_mode",
 291                                                        modes[j])) {
 292                                        mode_idx = j;
 293                                        break;
 294                                }
 295                        }
 296
 297                        for (j = 0; j < ARRAY_SIZE(phys); j++) {
 298                                if (hwconfig_subarg_cmp(str, "phy_type",
 299                                                        phys[j])) {
 300                                        phy_idx = j;
 301                                        break;
 302                                }
 303                        }
 304
 305                        if (mode_idx < 0 && phy_idx < 0) {
 306                                printf("WARNING: invalid phy or mode\n");
 307                                return;
 308                        }
 309
 310                        if (mode_idx > -1)
 311                                dr_mode_type = modes[mode_idx];
 312
 313                        if (phy_idx > -1)
 314                                dr_phy_type = phys[phy_idx];
 315                }
 316
 317                if (has_dual_phy())
 318                        dr_phy_type = phys[2];
 319
 320                usb_mode_off = fdt_fixup_usb_mode_phy_type(blob,
 321                                                           dr_mode_type, NULL,
 322                                                           usb_mode_off);
 323
 324                if (usb_mode_off < 0)
 325                        return;
 326
 327                usb_phy_off = fdt_fixup_usb_mode_phy_type(blob,
 328                                                          NULL, dr_phy_type,
 329                                                          usb_phy_off);
 330
 331                if (usb_phy_off < 0)
 332                        return;
 333
 334                if (has_erratum_a006261()) {
 335                        usb_erratum_a006261_off =  fdt_fixup_usb_erratum
 336                                                   (blob,
 337                                                    "fsl,usb-erratum-a006261",
 338                                                    usb_erratum_a006261_off);
 339                        if (usb_erratum_a006261_off < 0)
 340                                return;
 341                }
 342
 343                if (has_erratum_a007075()) {
 344                        usb_erratum_a007075_off =  fdt_fixup_usb_erratum
 345                                                   (blob,
 346                                                    "fsl,usb-erratum-a007075",
 347                                                    usb_erratum_a007075_off);
 348                        if (usb_erratum_a007075_off < 0)
 349                                return;
 350                }
 351
 352                if (has_erratum_a007792()) {
 353                        usb_erratum_a007792_off =  fdt_fixup_usb_erratum
 354                                                   (blob,
 355                                                    "fsl,usb-erratum-a007792",
 356                                                    usb_erratum_a007792_off);
 357                        if (usb_erratum_a007792_off < 0)
 358                                return;
 359                }
 360                if (has_erratum_a005697()) {
 361                        usb_erratum_a005697_off =  fdt_fixup_usb_erratum
 362                                                   (blob,
 363                                                    "fsl,usb-erratum-a005697",
 364                                                    usb_erratum_a005697_off);
 365                        if (usb_erratum_a005697_off < 0)
 366                                return;
 367                }
 368        }
 369}
 370#endif
 371