Linux-libre 5.3-gnu
[librecmc/linux-libre.git] / drivers / usb / phy / phy-ulpi.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Generic ULPI USB transceiver support
4  *
5  * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
6  *
7  * Based on sources from
8  *
9  *   Sascha Hauer <s.hauer@pengutronix.de>
10  *   Freescale Semiconductors
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/slab.h>
15 #include <linux/export.h>
16 #include <linux/usb.h>
17 #include <linux/usb/otg.h>
18 #include <linux/usb/ulpi.h>
19
20
21 struct ulpi_info {
22         unsigned int    id;
23         char            *name;
24 };
25
26 #define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
27 #define ULPI_INFO(_id, _name)           \
28         {                               \
29                 .id     = (_id),        \
30                 .name   = (_name),      \
31         }
32
33 /* ULPI hardcoded IDs, used for probing */
34 static struct ulpi_info ulpi_ids[] = {
35         ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
36         ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
37         ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
38         ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
39         ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
40 };
41
42 static int ulpi_set_otg_flags(struct usb_phy *phy)
43 {
44         unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
45                              ULPI_OTG_CTRL_DM_PULLDOWN;
46
47         if (phy->flags & ULPI_OTG_ID_PULLUP)
48                 flags |= ULPI_OTG_CTRL_ID_PULLUP;
49
50         /*
51          * ULPI Specification rev.1.1 default
52          * for Dp/DmPulldown is enabled.
53          */
54         if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
55                 flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
56
57         if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
58                 flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
59
60         if (phy->flags & ULPI_OTG_EXTVBUSIND)
61                 flags |= ULPI_OTG_CTRL_EXTVBUSIND;
62
63         return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
64 }
65
66 static int ulpi_set_fc_flags(struct usb_phy *phy)
67 {
68         unsigned int flags = 0;
69
70         /*
71          * ULPI Specification rev.1.1 default
72          * for XcvrSelect is Full Speed.
73          */
74         if (phy->flags & ULPI_FC_HS)
75                 flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
76         else if (phy->flags & ULPI_FC_LS)
77                 flags |= ULPI_FUNC_CTRL_LOW_SPEED;
78         else if (phy->flags & ULPI_FC_FS4LS)
79                 flags |= ULPI_FUNC_CTRL_FS4LS;
80         else
81                 flags |= ULPI_FUNC_CTRL_FULL_SPEED;
82
83         if (phy->flags & ULPI_FC_TERMSEL)
84                 flags |= ULPI_FUNC_CTRL_TERMSELECT;
85
86         /*
87          * ULPI Specification rev.1.1 default
88          * for OpMode is Normal Operation.
89          */
90         if (phy->flags & ULPI_FC_OP_NODRV)
91                 flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
92         else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
93                 flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
94         else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
95                 flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
96         else
97                 flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
98
99         /*
100          * ULPI Specification rev.1.1 default
101          * for SuspendM is Powered.
102          */
103         flags |= ULPI_FUNC_CTRL_SUSPENDM;
104
105         return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
106 }
107
108 static int ulpi_set_ic_flags(struct usb_phy *phy)
109 {
110         unsigned int flags = 0;
111
112         if (phy->flags & ULPI_IC_AUTORESUME)
113                 flags |= ULPI_IFC_CTRL_AUTORESUME;
114
115         if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
116                 flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
117
118         if (phy->flags & ULPI_IC_IND_PASSTHRU)
119                 flags |= ULPI_IFC_CTRL_PASSTHRU;
120
121         if (phy->flags & ULPI_IC_PROTECT_DIS)
122                 flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
123
124         return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
125 }
126
127 static int ulpi_set_flags(struct usb_phy *phy)
128 {
129         int ret;
130
131         ret = ulpi_set_otg_flags(phy);
132         if (ret)
133                 return ret;
134
135         ret = ulpi_set_ic_flags(phy);
136         if (ret)
137                 return ret;
138
139         return ulpi_set_fc_flags(phy);
140 }
141
142 static int ulpi_check_integrity(struct usb_phy *phy)
143 {
144         int ret, i;
145         unsigned int val = 0x55;
146
147         for (i = 0; i < 2; i++) {
148                 ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
149                 if (ret < 0)
150                         return ret;
151
152                 ret = usb_phy_io_read(phy, ULPI_SCRATCH);
153                 if (ret < 0)
154                         return ret;
155
156                 if (ret != val) {
157                         pr_err("ULPI integrity check: failed!");
158                         return -ENODEV;
159                 }
160                 val = val << 1;
161         }
162
163         pr_info("ULPI integrity check: passed.\n");
164
165         return 0;
166 }
167
168 static int ulpi_init(struct usb_phy *phy)
169 {
170         int i, vid, pid, ret;
171         u32 ulpi_id = 0;
172
173         for (i = 0; i < 4; i++) {
174                 ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
175                 if (ret < 0)
176                         return ret;
177                 ulpi_id = (ulpi_id << 8) | ret;
178         }
179         vid = ulpi_id & 0xffff;
180         pid = ulpi_id >> 16;
181
182         pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
183
184         for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
185                 if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
186                         pr_info("Found %s ULPI transceiver.\n",
187                                 ulpi_ids[i].name);
188                         break;
189                 }
190         }
191
192         ret = ulpi_check_integrity(phy);
193         if (ret)
194                 return ret;
195
196         return ulpi_set_flags(phy);
197 }
198
199 static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
200 {
201         struct usb_phy *phy = otg->usb_phy;
202         unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
203
204         if (!host) {
205                 otg->host = NULL;
206                 return 0;
207         }
208
209         otg->host = host;
210
211         flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
212                    ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
213                    ULPI_IFC_CTRL_CARKITMODE);
214
215         if (phy->flags & ULPI_IC_6PIN_SERIAL)
216                 flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
217         else if (phy->flags & ULPI_IC_3PIN_SERIAL)
218                 flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
219         else if (phy->flags & ULPI_IC_CARKIT)
220                 flags |= ULPI_IFC_CTRL_CARKITMODE;
221
222         return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
223 }
224
225 static int ulpi_set_vbus(struct usb_otg *otg, bool on)
226 {
227         struct usb_phy *phy = otg->usb_phy;
228         unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
229
230         flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
231
232         if (on) {
233                 if (phy->flags & ULPI_OTG_DRVVBUS)
234                         flags |= ULPI_OTG_CTRL_DRVVBUS;
235
236                 if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
237                         flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
238         }
239
240         return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
241 }
242
243 struct usb_phy *
244 otg_ulpi_create(struct usb_phy_io_ops *ops,
245                 unsigned int flags)
246 {
247         struct usb_phy *phy;
248         struct usb_otg *otg;
249
250         phy = kzalloc(sizeof(*phy), GFP_KERNEL);
251         if (!phy)
252                 return NULL;
253
254         otg = kzalloc(sizeof(*otg), GFP_KERNEL);
255         if (!otg) {
256                 kfree(phy);
257                 return NULL;
258         }
259
260         phy->label      = "ULPI";
261         phy->flags      = flags;
262         phy->io_ops     = ops;
263         phy->otg        = otg;
264         phy->init       = ulpi_init;
265
266         otg->usb_phy    = phy;
267         otg->set_host   = ulpi_set_host;
268         otg->set_vbus   = ulpi_set_vbus;
269
270         return phy;
271 }
272 EXPORT_SYMBOL_GPL(otg_ulpi_create);
273