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 fdt_fixup_usb_erratum(void *blob, const char *prop_erratum, 90 const char *controller_type, 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 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 = 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 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 = fdt_fixup_erratum(&usb_erratum_a006261_off, blob, 202 CHIPIDEA_USB2, "a006261", 203 has_erratum_a006261); 204 if (ret == -ENOSPC) 205 return; 206 ret = fdt_fixup_erratum(&usb_erratum_a007075_off, blob, 207 CHIPIDEA_USB2, "a007075", 208 has_erratum_a007075); 209 if (ret == -ENOSPC) 210 return; 211 ret = fdt_fixup_erratum(&usb_erratum_a007792_off, blob, 212 CHIPIDEA_USB2, "a007792", 213 has_erratum_a007792); 214 if (ret == -ENOSPC) 215 return; 216 ret = fdt_fixup_erratum(&usb_erratum_a005697_off, blob, 217 CHIPIDEA_USB2, "a005697", 218 has_erratum_a005697); 219 if (ret == -ENOSPC) 220 return; 221 ret = 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