Merge tag 'u-boot-imx-20200121' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[platform/kernel/u-boot.git] / drivers / phy / omap-usb2-phy.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * OMAP USB2 PHY LAYER
4  *
5  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
6  * Written by Jean-Jacques Hiblot <jjhiblot@ti.com>
7  */
8
9 #include <common.h>
10 #include <asm/io.h>
11 #include <dm.h>
12 #include <errno.h>
13 #include <generic-phy.h>
14 #include <regmap.h>
15 #include <syscon.h>
16
17 #define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT    BIT(0)
18
19 #define OMAP_DEV_PHY_PD         BIT(0)
20 #define OMAP_USB2_PHY_PD        BIT(28)
21
22 #define AM437X_USB2_PHY_PD              BIT(0)
23 #define AM437X_USB2_OTG_PD              BIT(1)
24 #define AM437X_USB2_OTGVDET_EN          BIT(19)
25 #define AM437X_USB2_OTGSESSEND_EN       BIT(20)
26
27 #define USB2PHY_DISCON_BYP_LATCH        BIT(31)
28 #define USB2PHY_ANA_CONFIG1             (0x4c)
29
30 #define AM654_USB2_OTG_PD               BIT(8)
31 #define AM654_USB2_VBUS_DET_EN          BIT(5)
32 #define AM654_USB2_VBUSVALID_DET_EN     BIT(4)
33
34 DECLARE_GLOBAL_DATA_PTR;
35
36 struct omap_usb2_phy {
37         struct regmap *pwr_regmap;
38         ulong flags;
39         void *phy_base;
40         u32 pwr_reg_offset;
41 };
42
43 struct usb_phy_data {
44         const char *label;
45         u8 flags;
46         u32 mask;
47         u32 power_on;
48         u32 power_off;
49 };
50
51 static const struct usb_phy_data omap5_usb2_data = {
52         .label = "omap5_usb2",
53         .flags = 0,
54         .mask = OMAP_DEV_PHY_PD,
55         .power_off = OMAP_DEV_PHY_PD,
56 };
57
58 static const struct usb_phy_data dra7x_usb2_data = {
59         .label = "dra7x_usb2",
60         .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
61         .mask = OMAP_DEV_PHY_PD,
62         .power_off = OMAP_DEV_PHY_PD,
63 };
64
65 static const struct usb_phy_data dra7x_usb2_phy2_data = {
66         .label = "dra7x_usb2_phy2",
67         .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
68         .mask = OMAP_USB2_PHY_PD,
69         .power_off = OMAP_USB2_PHY_PD,
70 };
71
72 static const struct usb_phy_data am437x_usb2_data = {
73         .label = "am437x_usb2",
74         .flags =  0,
75         .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
76                 AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
77         .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
78         .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
79 };
80
81 static const struct usb_phy_data am654_usb2_data = {
82         .label = "am654_usb2",
83         .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
84         .mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
85                 AM654_USB2_VBUSVALID_DET_EN,
86         .power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
87         .power_off = AM654_USB2_OTG_PD,
88 };
89
90 static const struct udevice_id omap_usb2_id_table[] = {
91         {
92                 .compatible = "ti,omap5-usb2",
93                 .data = (ulong)&omap5_usb2_data,
94         },
95         {
96                 .compatible = "ti,dra7x-usb2",
97                 .data = (ulong)&dra7x_usb2_data,
98         },
99         {
100                 .compatible = "ti,dra7x-usb2-phy2",
101                 .data = (ulong)&dra7x_usb2_phy2_data,
102         },
103         {
104                 .compatible = "ti,am437x-usb2",
105                 .data = (ulong)&am437x_usb2_data,
106         },
107         {
108                 .compatible = "ti,am654-usb2",
109                 .data = (ulong)&am654_usb2_data,
110         },
111         {},
112 };
113
114 static int omap_usb_phy_power(struct phy *usb_phy, bool on)
115 {
116         struct udevice *dev = usb_phy->dev;
117         const struct usb_phy_data *data;
118         const struct omap_usb2_phy *phy = dev_get_priv(dev);
119         u32 val;
120         int rc;
121
122         data = (const struct usb_phy_data *)dev_get_driver_data(dev);
123         if (!data)
124                 return -EINVAL;
125
126         rc = regmap_read(phy->pwr_regmap, phy->pwr_reg_offset, &val);
127         if (rc)
128                 return rc;
129         val &= ~data->mask;
130         if (on)
131                 val |= data->power_on;
132         else
133                 val |= data->power_off;
134         rc = regmap_write(phy->pwr_regmap, phy->pwr_reg_offset, val);
135         if (rc)
136                 return rc;
137
138         return 0;
139 }
140
141 static int omap_usb2_phy_init(struct phy *usb_phy)
142 {
143         struct udevice *dev = usb_phy->dev;
144         struct omap_usb2_phy *priv = dev_get_priv(dev);
145         u32 val;
146
147         if (priv->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
148                 /*
149                  *
150                  * Reduce the sensitivity of internal PHY by enabling the
151                  * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
152                  * resolves issues with certain devices which can otherwise
153                  * be prone to false disconnects.
154                  *
155                  */
156                 val = readl(priv->phy_base + USB2PHY_ANA_CONFIG1);
157                 val |= USB2PHY_DISCON_BYP_LATCH;
158                 writel(val, priv->phy_base + USB2PHY_ANA_CONFIG1);
159         }
160
161         return 0;
162 }
163
164 static int omap_usb2_phy_power_on(struct phy *usb_phy)
165 {
166         return omap_usb_phy_power(usb_phy, true);
167 }
168
169 static int omap_usb2_phy_power_off(struct phy *usb_phy)
170 {
171         return omap_usb_phy_power(usb_phy, false);
172 }
173
174 static int omap_usb2_phy_exit(struct phy *usb_phy)
175 {
176         return omap_usb_phy_power(usb_phy, false);
177 }
178
179 struct phy_ops omap_usb2_phy_ops = {
180         .init = omap_usb2_phy_init,
181         .power_on = omap_usb2_phy_power_on,
182         .power_off = omap_usb2_phy_power_off,
183         .exit = omap_usb2_phy_exit,
184 };
185
186 int omap_usb2_phy_probe(struct udevice *dev)
187 {
188         int rc;
189         struct regmap *regmap;
190         struct omap_usb2_phy *priv = dev_get_priv(dev);
191         const struct usb_phy_data *data;
192         u32 tmp[2];
193
194         data = (const struct usb_phy_data *)dev_get_driver_data(dev);
195         if (!data)
196                 return -EINVAL;
197
198         if (data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
199                 priv->phy_base = dev_read_addr_ptr(dev);
200
201                 if (!priv->phy_base)
202                         return -EINVAL;
203                 priv->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
204         }
205
206         regmap = syscon_regmap_lookup_by_phandle(dev, "syscon-phy-power");
207         if (!IS_ERR(regmap)) {
208                 priv->pwr_regmap = regmap;
209                 rc =  dev_read_u32_array(dev, "syscon-phy-power", tmp, 2);
210                 if (rc) {
211                         printf("couldn't get power reg. offset (err %d)\n", rc);
212                         return rc;
213                 }
214                 priv->pwr_reg_offset = tmp[1];
215                 return 0;
216         }
217         regmap = syscon_regmap_lookup_by_phandle(dev, "ctrl-module");
218         if (!IS_ERR(regmap)) {
219                 priv->pwr_regmap = regmap;
220                 priv->pwr_reg_offset = 0;
221                 return 0;
222         }
223
224         printf("can't get regmap (err %ld)\n", PTR_ERR(regmap));
225         return PTR_ERR(regmap);
226 }
227
228 U_BOOT_DRIVER(omap_usb2_phy) = {
229         .name = "omap_usb2_phy",
230         .id = UCLASS_PHY,
231         .of_match = omap_usb2_id_table,
232         .probe = omap_usb2_phy_probe,
233         .ops = &omap_usb2_phy_ops,
234         .priv_auto_alloc_size = sizeof(struct omap_usb2_phy),
235 };