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 }; 197