Merge tag 'for-master-20181210' of git://git.denx.de/u-boot-rockchip
[oweals/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 USB2PHY_DISCON_BYP_LATCH        BIT(31)
23 #define USB2PHY_ANA_CONFIG1             (0x4c)
24
25 DECLARE_GLOBAL_DATA_PTR;
26
27 struct omap_usb2_phy {
28         struct regmap *pwr_regmap;
29         ulong flags;
30         void *phy_base;
31         u32 pwr_reg_offset;
32 };
33
34 struct usb_phy_data {
35         const char *label;
36         u8 flags;
37         u32 mask;
38         u32 power_on;
39         u32 power_off;
40 };
41
42 static const struct usb_phy_data omap5_usb2_data = {
43         .label = "omap5_usb2",
44         .flags = 0,
45         .mask = OMAP_DEV_PHY_PD,
46         .power_off = OMAP_DEV_PHY_PD,
47 };
48
49 static const struct usb_phy_data dra7x_usb2_data = {
50         .label = "dra7x_usb2",
51         .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
52         .mask = OMAP_DEV_PHY_PD,
53         .power_off = OMAP_DEV_PHY_PD,
54 };
55
56 static const struct usb_phy_data dra7x_usb2_phy2_data = {
57         .label = "dra7x_usb2_phy2",
58         .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
59         .mask = OMAP_USB2_PHY_PD,
60         .power_off = OMAP_USB2_PHY_PD,
61 };
62
63 static const struct udevice_id omap_usb2_id_table[] = {
64         {
65                 .compatible = "ti,omap5-usb2",
66                 .data = (ulong)&omap5_usb2_data,
67         },
68         {
69                 .compatible = "ti,dra7x-usb2",
70                 .data = (ulong)&dra7x_usb2_data,
71         },
72         {
73                 .compatible = "ti,dra7x-usb2-phy2",
74                 .data = (ulong)&dra7x_usb2_phy2_data,
75         },
76         {},
77 };
78
79 static int omap_usb_phy_power(struct phy *usb_phy, bool on)
80 {
81         struct udevice *dev = usb_phy->dev;
82         const struct usb_phy_data *data;
83         const struct omap_usb2_phy *phy = dev_get_priv(dev);
84         u32 val;
85         int rc;
86
87         data = (const struct usb_phy_data *)dev_get_driver_data(dev);
88         if (!data)
89                 return -EINVAL;
90
91         rc = regmap_read(phy->pwr_regmap, phy->pwr_reg_offset, &val);
92         if (rc)
93                 return rc;
94         val &= ~data->mask;
95         if (on)
96                 val |= data->power_on;
97         else
98                 val |= data->power_off;
99         rc = regmap_write(phy->pwr_regmap, phy->pwr_reg_offset, val);
100         if (rc)
101                 return rc;
102
103         return 0;
104 }
105
106 static int omap_usb2_phy_init(struct phy *usb_phy)
107 {
108         struct udevice *dev = usb_phy->dev;
109         struct omap_usb2_phy *priv = dev_get_priv(dev);
110         u32 val;
111
112         if (priv->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
113                 /*
114                  *
115                  * Reduce the sensitivity of internal PHY by enabling the
116                  * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
117                  * resolves issues with certain devices which can otherwise
118                  * be prone to false disconnects.
119                  *
120                  */
121                 val = readl(priv->phy_base + USB2PHY_ANA_CONFIG1);
122                 val |= USB2PHY_DISCON_BYP_LATCH;
123                 writel(val, priv->phy_base + USB2PHY_ANA_CONFIG1);
124         }
125
126         return 0;
127 }
128
129 static int omap_usb2_phy_power_on(struct phy *usb_phy)
130 {
131         return omap_usb_phy_power(usb_phy, true);
132 }
133
134 static int omap_usb2_phy_power_off(struct phy *usb_phy)
135 {
136         return omap_usb_phy_power(usb_phy, false);
137 }
138
139 static int omap_usb2_phy_exit(struct phy *usb_phy)
140 {
141         return omap_usb_phy_power(usb_phy, false);
142 }
143
144 struct phy_ops omap_usb2_phy_ops = {
145         .init = omap_usb2_phy_init,
146         .power_on = omap_usb2_phy_power_on,
147         .power_off = omap_usb2_phy_power_off,
148         .exit = omap_usb2_phy_exit,
149 };
150
151 int omap_usb2_phy_probe(struct udevice *dev)
152 {
153         int rc;
154         struct regmap *regmap;
155         struct omap_usb2_phy *priv = dev_get_priv(dev);
156         const struct usb_phy_data *data;
157         u32 tmp[2];
158
159         data = (const struct usb_phy_data *)dev_get_driver_data(dev);
160         if (!data)
161                 return -EINVAL;
162
163         if (data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
164                 u32 base = dev_read_addr(dev);
165
166                 if (base == FDT_ADDR_T_NONE)
167                         return -EINVAL;
168                 priv->phy_base = (void *)base;
169                 priv->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
170         }
171
172         regmap = syscon_regmap_lookup_by_phandle(dev, "syscon-phy-power");
173         if (IS_ERR(regmap)) {
174                 printf("can't get regmap (err %ld)\n", PTR_ERR(regmap));
175                 return PTR_ERR(regmap);
176         }
177         priv->pwr_regmap = regmap;
178
179         rc =  dev_read_u32_array(dev, "syscon-phy-power", tmp, 2);
180         if (rc) {
181                 printf("couldn't get power reg. offset (err %d)\n", rc);
182                 return rc;
183         }
184         priv->pwr_reg_offset = tmp[1];
185
186         return 0;
187 }
188
189 U_BOOT_DRIVER(omap_usb2_phy) = {
190         .name = "omap_usb2_phy",
191         .id = UCLASS_PHY,
192         .of_match = omap_usb2_id_table,
193         .probe = omap_usb2_phy_probe,
194         .ops = &omap_usb2_phy_ops,
195         .priv_auto_alloc_size = sizeof(struct omap_usb2_phy),
196 };