1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2022 Sumit Garg <sumit.garg@linaro.org> 4 * 5 * Based on Linux driver 6 */ 7 8#include <common.h> 9#include <dm.h> 10#include <generic-phy.h> 11#include <reset.h> 12#include <clk.h> 13#include <asm/io.h> 14#include <linux/delay.h> 15 16/* PHY register and bit definitions */ 17#define PHY_CTRL_COMMON0 0x078 18#define SIDDQ BIT(2) 19 20struct hsphy_init_seq { 21 int offset; 22 int val; 23 int delay; 24}; 25 26struct hsphy_data { 27 const struct hsphy_init_seq *init_seq; 28 unsigned int init_seq_num; 29}; 30 31struct hsphy_priv { 32 void __iomem *base; 33 struct clk_bulk clks; 34 struct reset_ctl phy_rst; 35 struct reset_ctl por_rst; 36 const struct hsphy_data *data; 37}; 38 39static int hsphy_power_on(struct phy *phy) 40{ 41 struct hsphy_priv *priv = dev_get_priv(phy->dev); 42 u32 val; 43 44 val = readb(priv->base + PHY_CTRL_COMMON0); 45 val &= ~SIDDQ; 46 writeb(val, priv->base + PHY_CTRL_COMMON0); 47 48 return 0; 49} 50 51static int hsphy_power_off(struct phy *phy) 52{ 53 struct hsphy_priv *priv = dev_get_priv(phy->dev); 54 u32 val; 55 56 val = readb(priv->base + PHY_CTRL_COMMON0); 57 val |= SIDDQ; 58 writeb(val, priv->base + PHY_CTRL_COMMON0); 59 60 return 0; 61} 62 63static int hsphy_reset(struct hsphy_priv *priv) 64{ 65 int ret; 66 67 ret = reset_assert(&priv->phy_rst); 68 if (ret) 69 return ret; 70 71 udelay(10); 72 73 ret = reset_deassert(&priv->phy_rst); 74 if (ret) 75 return ret; 76 77 udelay(80); 78 79 return 0; 80} 81 82static void hsphy_init_sequence(struct hsphy_priv *priv) 83{ 84 const struct hsphy_data *data = priv->data; 85 const struct hsphy_init_seq *seq; 86 int i; 87 88 /* Device match data is optional. */ 89 if (!data) 90 return; 91 92 seq = data->init_seq; 93 94 for (i = 0; i < data->init_seq_num; i++, seq++) { 95 writeb(seq->val, priv->base + seq->offset); 96 if (seq->delay) 97 udelay(seq->delay); 98 } 99} 100 101static int hsphy_por_reset(struct hsphy_priv *priv) 102{ 103 int ret; 104 u32 val; 105 106 ret = reset_assert(&priv->por_rst); 107 if (ret) 108 return ret; 109 110 /* 111 * The Femto PHY is POR reset in the following scenarios. 112 * 113 * 1. After overriding the parameter registers. 114 * 2. Low power mode exit from PHY retention. 115 * 116 * Ensure that SIDDQ is cleared before bringing the PHY 117 * out of reset. 118 */ 119 val = readb(priv->base + PHY_CTRL_COMMON0); 120 val &= ~SIDDQ; 121 writeb(val, priv->base + PHY_CTRL_COMMON0); 122 123 /* 124 * As per databook, 10 usec delay is required between 125 * PHY POR assert and de-assert. 126 */ 127 udelay(10); 128 ret = reset_deassert(&priv->por_rst); 129 if (ret) 130 return ret; 131 132 /* 133 * As per databook, it takes 75 usec for PHY to stabilize 134 * after the reset. 135 */ 136 udelay(80); 137 138 return 0; 139} 140 141static int hsphy_clk_init(struct udevice *dev, struct hsphy_priv *priv) 142{ 143 int ret; 144 145 ret = clk_get_bulk(dev, &priv->clks); 146 if (ret == -ENOSYS || ret == -ENOENT) 147 return 0; 148 if (ret) 149 return ret; 150 151 ret = clk_enable_bulk(&priv->clks); 152 if (ret) { 153 clk_release_bulk(&priv->clks); 154 return ret; 155 } 156 157 return 0; 158} 159 160static int hsphy_init(struct phy *phy) 161{ 162 struct hsphy_priv *priv = dev_get_priv(phy->dev); 163 int ret; 164 165 ret = hsphy_clk_init(phy->dev, priv); 166 if (ret) 167 return ret; 168 169 ret = hsphy_reset(priv); 170 if (ret) 171 return ret; 172 173 hsphy_init_sequence(priv); 174 175 hsphy_por_reset(priv); 176 if (ret) 177 return ret; 178 179 return 0; 180} 181 182static int hsphy_probe(struct udevice *dev) 183{ 184 struct hsphy_priv *priv = dev_get_priv(dev); 185 int ret; 186 187 priv->base = dev_read_addr_ptr(dev); 188 if (!priv->base) 189 return -EINVAL; 190 191 ret = reset_get_by_name(dev, "phy", &priv->phy_rst); 192 if (ret) 193 return ret; 194 195 ret = reset_get_by_name(dev, "por", &priv->por_rst); 196 if (ret) 197 return ret; 198 199 priv->data = (const struct hsphy_data *)dev_get_driver_data(dev); 200 201 return 0; 202} 203 204static struct phy_ops hsphy_ops = { 205 .power_on = hsphy_power_on, 206 .power_off = hsphy_power_off, 207 .init = hsphy_init, 208}; 209 210/* 211 * The macro is used to define an initialization sequence. Each tuple 212 * is meant to program 'value' into phy register at 'offset' with 'delay' 213 * in us followed. 214 */ 215#define HSPHY_INIT_CFG(o, v, d) { .offset = o, .val = v, .delay = d, } 216 217static const struct hsphy_init_seq init_seq_femtophy[] = { 218 HSPHY_INIT_CFG(0xc0, 0x01, 0), 219 HSPHY_INIT_CFG(0xe8, 0x0d, 0), 220 HSPHY_INIT_CFG(0x74, 0x12, 0), 221 HSPHY_INIT_CFG(0x98, 0x63, 0), 222 HSPHY_INIT_CFG(0x9c, 0x03, 0), 223 HSPHY_INIT_CFG(0xa0, 0x1d, 0), 224 HSPHY_INIT_CFG(0xa4, 0x03, 0), 225 HSPHY_INIT_CFG(0x8c, 0x23, 0), 226 HSPHY_INIT_CFG(0x78, 0x08, 0), 227 HSPHY_INIT_CFG(0x7c, 0xdc, 0), 228 HSPHY_INIT_CFG(0x90, 0xe0, 20), 229 HSPHY_INIT_CFG(0x74, 0x10, 0), 230 HSPHY_INIT_CFG(0x90, 0x60, 0), 231}; 232 233static const struct hsphy_data data_femtophy = { 234 .init_seq = init_seq_femtophy, 235 .init_seq_num = ARRAY_SIZE(init_seq_femtophy), 236}; 237 238static const struct udevice_id hsphy_ids[] = { 239 { .compatible = "qcom,usb-hs-28nm-femtophy", .data = (ulong)&data_femtophy }, 240 { } 241}; 242 243U_BOOT_DRIVER(qcom_usb_hs_28nm) = { 244 .name = "qcom-usb-hs-28nm", 245 .id = UCLASS_PHY, 246 .of_match = hsphy_ids, 247 .ops = &hsphy_ops, 248 .probe = hsphy_probe, 249 .priv_auto = sizeof(struct hsphy_priv), 250}; 251