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