1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc. 4 * 5 * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB 6 * 7 * Author: Tor Krill tor@excito.com 8 */ 9 10#include <common.h> 11#include <log.h> 12#include <usb.h> 13#include <asm/io.h> 14#include <hwconfig.h> 15#include <fsl_errata.h> 16#include <fsl_usb.h> 17#include <fdt_support.h> 18 19/* USB Controllers */ 20#define FSL_USB2_MPH "fsl-usb2-mph" 21#define FSL_USB2_DR "fsl-usb2-dr" 22#define CHIPIDEA_USB2 "chipidea,usb2" 23#define SNPS_DWC3 "snps,dwc3" 24 25static const char * const compat_usb_fsl[] = { 26 FSL_USB2_MPH, 27 FSL_USB2_DR, 28 SNPS_DWC3, 29 NULL 30}; 31 32static int fdt_usb_get_node_type(void *blob, int start_offset, 33 int *node_offset, const char **node_type) 34{ 35 int i; 36 int ret = -ENOENT; 37 38 for (i = 0; compat_usb_fsl[i]; i++) { 39 *node_offset = fdt_node_offset_by_compatible 40 (blob, start_offset, 41 compat_usb_fsl[i]); 42 if (*node_offset >= 0) { 43 *node_type = compat_usb_fsl[i]; 44 ret = 0; 45 break; 46 } 47 } 48 49 return ret; 50} 51 52static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode, 53 const char *phy_type, int start_offset) 54{ 55 const char *prop_mode = "dr_mode"; 56 const char *prop_type = "phy_type"; 57 const char *node_type = NULL; 58 int node_offset; 59 int err; 60 61 err = fdt_usb_get_node_type(blob, start_offset, 62 &node_offset, &node_type); 63 if (err < 0) 64 return err; 65 66 if (mode) { 67 err = fdt_setprop(blob, node_offset, prop_mode, mode, 68 strlen(mode) + 1); 69 if (err < 0) 70 printf("WARNING: could not set %s for %s: %s.\n", 71 prop_mode, node_type, fdt_strerror(err)); 72 } 73 74 if (phy_type) { 75 err = fdt_setprop(blob, node_offset, prop_type, phy_type, 76 strlen(phy_type) + 1); 77 if (err < 0) 78 printf("WARNING: could not set %s for %s: %s.\n", 79 prop_type, node_type, fdt_strerror(err)); 80 } 81 82 return node_offset; 83} 84 85static int fsl_fdt_fixup_usb_erratum(void *blob, const char *prop_erratum, 86 const char *controller_type, 87 int start_offset) 88{ 89 int node_offset, err; 90 const char *node_type = NULL; 91 const char *node_name = NULL; 92 93 err = fdt_usb_get_node_type(blob, start_offset, 94 &node_offset, &node_type); 95 if (err < 0) 96 return err; 97 98 if (!strcmp(node_type, FSL_USB2_MPH) || !strcmp(node_type, FSL_USB2_DR)) 99 node_name = CHIPIDEA_USB2; 100 else 101 node_name = node_type; 102 if (strcmp(node_name, controller_type)) 103 return err; 104 105 err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0); 106 if (err < 0) { 107 printf("ERROR: could not set %s for %s: %s.\n", 108 prop_erratum, node_type, fdt_strerror(err)); 109 } 110 111 return node_offset; 112} 113 114static int fsl_fdt_fixup_erratum(int *usb_erratum_off, void *blob, 115 const char *controller_type, char *str, 116 bool (*has_erratum)(void)) 117{ 118 char buf[32] = {0}; 119 120 snprintf(buf, sizeof(buf), "fsl,usb-erratum-%s", str); 121 if (!has_erratum()) 122 return -EINVAL; 123 *usb_erratum_off = fsl_fdt_fixup_usb_erratum(blob, buf, controller_type, 124 *usb_erratum_off); 125 if (*usb_erratum_off < 0) 126 return -ENOSPC; 127 debug("Adding USB erratum %s\n", str); 128 return 0; 129} 130 131void fsl_fdt_fixup_dr_usb(void *blob, struct bd_info *bd) 132{ 133 static const char * const modes[] = { "host", "peripheral", "otg" }; 134 static const char * const phys[] = { "ulpi", "utmi", "utmi_dual" }; 135 int usb_erratum_a006261_off = -1; 136 int usb_erratum_a007075_off = -1; 137 int usb_erratum_a007792_off = -1; 138 int usb_erratum_a005697_off = -1; 139 int usb_erratum_a008751_off = -1; 140 int usb_mode_off = -1; 141 int usb_phy_off = -1; 142 char str[5]; 143 int i, j; 144 int ret; 145 146 for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { 147 const char *dr_mode_type = NULL; 148 const char *dr_phy_type = NULL; 149 int mode_idx = -1, phy_idx = -1; 150 151 snprintf(str, 5, "%s%d", "usb", i); 152 if (hwconfig(str)) { 153 for (j = 0; j < ARRAY_SIZE(modes); j++) { 154 if (hwconfig_subarg_cmp(str, "dr_mode", 155 modes[j])) { 156 mode_idx = j; 157 break; 158 } 159 } 160 161 for (j = 0; j < ARRAY_SIZE(phys); j++) { 162 if (hwconfig_subarg_cmp(str, "phy_type", 163 phys[j])) { 164 phy_idx = j; 165 break; 166 } 167 } 168 169 if (mode_idx < 0 && phy_idx < 0) { 170 printf("WARNING: invalid phy or mode\n"); 171 return; 172 } 173 174 if (mode_idx > -1) 175 dr_mode_type = modes[mode_idx]; 176 177 if (phy_idx > -1) 178 dr_phy_type = phys[phy_idx]; 179 } 180 181 if (has_dual_phy()) 182 dr_phy_type = phys[2]; 183 184 usb_mode_off = fdt_fixup_usb_mode_phy_type(blob, 185 dr_mode_type, NULL, 186 usb_mode_off); 187 188 if (usb_mode_off < 0) 189 return; 190 191 usb_phy_off = fdt_fixup_usb_mode_phy_type(blob, 192 NULL, dr_phy_type, 193 usb_phy_off); 194 195 if (usb_phy_off < 0) 196 return; 197 198 ret = fsl_fdt_fixup_erratum(&usb_erratum_a006261_off, blob, 199 CHIPIDEA_USB2, "a006261", 200 has_erratum_a006261); 201 if (ret == -ENOSPC) 202 return; 203 ret = fsl_fdt_fixup_erratum(&usb_erratum_a007075_off, blob, 204 CHIPIDEA_USB2, "a007075", 205 has_erratum_a007075); 206 if (ret == -ENOSPC) 207 return; 208 ret = fsl_fdt_fixup_erratum(&usb_erratum_a007792_off, blob, 209 CHIPIDEA_USB2, "a007792", 210 has_erratum_a007792); 211 if (ret == -ENOSPC) 212 return; 213 ret = fsl_fdt_fixup_erratum(&usb_erratum_a005697_off, blob, 214 CHIPIDEA_USB2, "a005697", 215 has_erratum_a005697); 216 if (ret == -ENOSPC) 217 return; 218 ret = fsl_fdt_fixup_erratum(&usb_erratum_a008751_off, blob, 219 SNPS_DWC3, "a008751", 220 has_erratum_a008751); 221 if (ret == -ENOSPC) 222 return; 223 224 } 225} 226