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