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