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