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_mode_off = -1; 143 int usb_phy_off = -1; 144 char str[5]; 145 int i, j; 146 int ret; 147 148 for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { 149 const char *dr_mode_type = NULL; 150 const char *dr_phy_type = NULL; 151 int mode_idx = -1, phy_idx = -1; 152 153 snprintf(str, 5, "%s%d", "usb", i); 154 if (hwconfig(str)) { 155 for (j = 0; j < ARRAY_SIZE(modes); j++) { 156 if (hwconfig_subarg_cmp(str, "dr_mode", 157 modes[j])) { 158 mode_idx = j; 159 break; 160 } 161 } 162 163 for (j = 0; j < ARRAY_SIZE(phys); j++) { 164 if (hwconfig_subarg_cmp(str, "phy_type", 165 phys[j])) { 166 phy_idx = j; 167 break; 168 } 169 } 170 171 if (mode_idx < 0 && phy_idx < 0) { 172 printf("WARNING: invalid phy or mode\n"); 173 return; 174 } 175 176 if (mode_idx > -1) 177 dr_mode_type = modes[mode_idx]; 178 179 if (phy_idx > -1) 180 dr_phy_type = phys[phy_idx]; 181 } 182 183 if (has_dual_phy()) 184 dr_phy_type = phys[2]; 185 186 usb_mode_off = fdt_fixup_usb_mode_phy_type(blob, 187 dr_mode_type, NULL, 188 usb_mode_off); 189 190 if (usb_mode_off < 0) 191 return; 192 193 usb_phy_off = fdt_fixup_usb_mode_phy_type(blob, 194 NULL, dr_phy_type, 195 usb_phy_off); 196 197 if (usb_phy_off < 0) 198 return; 199 200 ret = fdt_fixup_erratum(&usb_erratum_a006261_off, blob, 201 CHIPIDEA_USB2, "a006261", 202 has_erratum_a006261); 203 if (ret == -ENOSPC) 204 return; 205 ret = fdt_fixup_erratum(&usb_erratum_a007075_off, blob, 206 CHIPIDEA_USB2, "a007075", 207 has_erratum_a007075); 208 if (ret == -ENOSPC) 209 return; 210 ret = fdt_fixup_erratum(&usb_erratum_a007792_off, blob, 211 CHIPIDEA_USB2, "a007792", 212 has_erratum_a007792); 213 if (ret == -ENOSPC) 214 return; 215 ret = fdt_fixup_erratum(&usb_erratum_a005697_off, blob, 216 CHIPIDEA_USB2, "a005697", 217 has_erratum_a005697); 218 if (ret == -ENOSPC) 219 return; 220 } 221 } 222