1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Salvo PHY is a 28nm PHY, it is a legacy PHY, and only
* for USB3 and USB2.
*
* Copyright (c) 2019-2020 NXP
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
/* PHY register definition */
#define PHY_PMA_CMN_CTRL1 0xC800
#define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0
#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084
#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR 0x0085
#define TB_ADDR_CMN_PLL0_INTDIV 0x0094
#define TB_ADDR_CMN_PLL0_FRACDIV 0x0095
#define TB_ADDR_CMN_PLL0_HIGH_THR 0x0096
#define TB_ADDR_CMN_PLL0_SS_CTRL1 0x0098
#define TB_ADDR_CMN_PLL0_SS_CTRL2 0x0099
#define TB_ADDR_CMN_PLL0_DSM_DIAG 0x0097
#define TB_ADDR_CMN_DIAG_PLL0_OVRD 0x01c2
#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD 0x01c0
#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD 0x01c1
#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE 0x01C5
#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE 0x01C6
#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG 0x01C7
#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE 0x01c4
#define TB_ADDR_CMN_PSM_CLK_CTRL 0x0061
#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR 0x40ea
#define TB_ADDR_XCVR_PSM_RCTRL 0x4001
#define TB_ADDR_TX_PSC_A0 0x4100
#define TB_ADDR_TX_PSC_A1 0x4101
#define TB_ADDR_TX_PSC_A2 0x4102
#define TB_ADDR_TX_PSC_A3 0x4103
#define TB_ADDR_TX_DIAG_ECTRL_OVRD 0x41f5
#define TB_ADDR_TX_PSC_CAL 0x4106
#define TB_ADDR_TX_PSC_RDY 0x4107
#define TB_ADDR_RX_PSC_A0 0x8000
#define TB_ADDR_RX_PSC_A1 0x8001
#define TB_ADDR_RX_PSC_A2 0x8002
#define TB_ADDR_RX_PSC_A3 0x8003
#define TB_ADDR_RX_PSC_CAL 0x8006
#define TB_ADDR_RX_PSC_RDY 0x8007
#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 0x4058
#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY 0x41e7
#define TB_ADDR_RX_SLC_CU_ITER_TMR 0x80e3
#define TB_ADDR_RX_SIGDET_HL_FILT_TMR 0x8090
#define TB_ADDR_RX_SAMP_DAC_CTRL 0x8058
#define TB_ADDR_RX_DIAG_SIGDET_TUNE 0x81dc
#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 0x81df
#define TB_ADDR_RX_DIAG_BS_TM 0x81f5
#define TB_ADDR_RX_DIAG_DFE_CTRL1 0x81d3
#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 0x81c7
#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 0x81c2
#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 0x81c1
#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 0x81c9
#define TB_ADDR_RX_DIAG_RXFE_TM3 0x81f8
#define TB_ADDR_RX_DIAG_RXFE_TM4 0x81f9
#define TB_ADDR_RX_DIAG_LFPSDET_TUNE 0x81dd
#define TB_ADDR_RX_DIAG_DFE_CTRL3 0x81d5
#define TB_ADDR_RX_DIAG_SC2C_DELAY 0x81e1
#define TB_ADDR_RX_REE_VGA_GAIN_NODFE 0x81bf
#define TB_ADDR_XCVR_PSM_CAL_TMR 0x4002
#define TB_ADDR_XCVR_PSM_A0BYP_TMR 0x4004
#define TB_ADDR_XCVR_PSM_A0IN_TMR 0x4003
#define TB_ADDR_XCVR_PSM_A1IN_TMR 0x4005
#define TB_ADDR_XCVR_PSM_A2IN_TMR 0x4006
#define TB_ADDR_XCVR_PSM_A3IN_TMR 0x4007
#define TB_ADDR_XCVR_PSM_A4IN_TMR 0x4008
#define TB_ADDR_XCVR_PSM_A5IN_TMR 0x4009
#define TB_ADDR_XCVR_PSM_A0OUT_TMR 0x400a
#define TB_ADDR_XCVR_PSM_A1OUT_TMR 0x400b
#define TB_ADDR_XCVR_PSM_A2OUT_TMR 0x400c
#define TB_ADDR_XCVR_PSM_A3OUT_TMR 0x400d
#define TB_ADDR_XCVR_PSM_A4OUT_TMR 0x400e
#define TB_ADDR_XCVR_PSM_A5OUT_TMR 0x400f
#define TB_ADDR_TX_RCVDET_EN_TMR 0x4122
#define TB_ADDR_TX_RCVDET_ST_TMR 0x4123
#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2
#define TB_ADDR_TX_RCVDETSC_CTRL 0x4124
/* TB_ADDR_TX_RCVDETSC_CTRL */
#define RXDET_IN_P3_32KHZ BIT(0)
struct cdns_reg_pairs {
u16 val;
u32 off;
};
struct cdns_salvo_data {
u8 reg_offset_shift;
const struct cdns_reg_pairs *init_sequence_val;
u8 init_sequence_length;
};
struct cdns_salvo_phy {
struct phy *phy;
struct clk *clk;
void __iomem *base;
struct cdns_salvo_data *data;
};
static const struct of_device_id cdns_salvo_phy_of_match[];
static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg)
{
return (u16)readl(salvo_phy->base +
reg * (1 << salvo_phy->data->reg_offset_shift));
}
static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy,
u32 reg, u16 val)
{
writel(val, salvo_phy->base +
reg * (1 << salvo_phy->data->reg_offset_shift));
}
/*
* Below bringup sequence pair are from Cadence PHY's User Guide
* and NXP platform tuning results.
*/
static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
{0x0830, PHY_PMA_CMN_CTRL1},
{0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL},
{0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR},
{0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR},
{0x00d0, TB_ADDR_CMN_PLL0_INTDIV},
{0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV},
{0x0034, TB_ADDR_CMN_PLL0_HIGH_THR},
{0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1},
{0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2},
{0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG},
{0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD},
{0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD},
{0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD},
{0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE},
{0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE},
{0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG},
{0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE},
{0x000a, TB_ADDR_CMN_PSM_CLK_CTRL},
{0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR},
{0xbefc, TB_ADDR_XCVR_PSM_RCTRL},
{0x7799, TB_ADDR_TX_PSC_A0},
{0x7798, TB_ADDR_TX_PSC_A1},
{0x509b, TB_ADDR_TX_PSC_A2},
{0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD},
{0x509b, TB_ADDR_TX_PSC_A3},
{0x2090, TB_ADDR_TX_PSC_CAL},
{0x2090, TB_ADDR_TX_PSC_RDY},
{0xA6FD, TB_ADDR_RX_PSC_A0},
{0xA6FD, TB_ADDR_RX_PSC_A1},
{0xA410, TB_ADDR_RX_PSC_A2},
{0x2410, TB_ADDR_RX_PSC_A3},
{0x23FF, TB_ADDR_RX_PSC_CAL},
{0x2010, TB_ADDR_RX_PSC_RDY},
{0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000},
{0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY},
{0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR},
{0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR},
{0x0000, TB_ADDR_RX_SAMP_DAC_CTRL},
{0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE},
{0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2},
{0x0480, TB_ADDR_RX_DIAG_BS_TM},
{0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1},
{0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4},
{0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0},
{0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0},
{0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6},
{0x8000, TB_ADDR_RX_DIAG_RXFE_TM3},
{0x0003, TB_ADDR_RX_DIAG_RXFE_TM4},
{0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE},
{0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3},
{0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY},
{0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE},
{0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR},
{0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR},
{0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR},
{0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR},
{0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR},
{0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR},
{0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR},
{0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR},
/* Change rx detect parameter */
{0x0960, TB_ADDR_TX_RCVDET_EN_TMR},
{0x01e0, TB_ADDR_TX_RCVDET_ST_TMR},
{0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR},
};
static int cdns_salvo_phy_init(struct phy *phy)
{
struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
struct cdns_salvo_data *data = salvo_phy->data;
int ret, i;
u16 value;
ret = clk_prepare_enable(salvo_phy->clk);
if (ret)
return ret;
for (i = 0; i < data->init_sequence_length; i++) {
const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val);
}
/* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL);
value |= RXDET_IN_P3_32KHZ;
cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL,
RXDET_IN_P3_32KHZ);
udelay(10);
clk_disable_unprepare(salvo_phy->clk);
return ret;
}
static int cdns_salvo_phy_power_on(struct phy *phy)
{
struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
return clk_prepare_enable(salvo_phy->clk);
}
static int cdns_salvo_phy_power_off(struct phy *phy)
{
struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
clk_disable_unprepare(salvo_phy->clk);
return 0;
}
static const struct phy_ops cdns_salvo_phy_ops = {
.init = cdns_salvo_phy_init,
.power_on = cdns_salvo_phy_power_on,
.power_off = cdns_salvo_phy_power_off,
.owner = THIS_MODULE,
};
static int cdns_salvo_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct cdns_salvo_phy *salvo_phy;
struct resource *res;
const struct of_device_id *match;
struct cdns_salvo_data *data;
match = of_match_device(cdns_salvo_phy_of_match, dev);
if (!match)
return -EINVAL;
data = (struct cdns_salvo_data *)match->data;
salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL);
if (!salvo_phy)
return -ENOMEM;
salvo_phy->data = data;
salvo_phy->clk = devm_clk_get_optional(dev, "salvo_phy_clk");
if (IS_ERR(salvo_phy->clk))
return PTR_ERR(salvo_phy->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
salvo_phy->base = devm_ioremap_resource(dev, res);
if (IS_ERR(salvo_phy->base))
return PTR_ERR(salvo_phy->base);
salvo_phy->phy = devm_phy_create(dev, NULL, &cdns_salvo_phy_ops);
if (IS_ERR(salvo_phy->phy))
return PTR_ERR(salvo_phy->phy);
phy_set_drvdata(salvo_phy->phy, salvo_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct cdns_salvo_data cdns_nxp_salvo_data = {
2,
cdns_nxp_sequence_pair,
ARRAY_SIZE(cdns_nxp_sequence_pair),
};
static const struct of_device_id cdns_salvo_phy_of_match[] = {
{
.compatible = "nxp,salvo-phy",
.data = &cdns_nxp_salvo_data,
},
{}
};
MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match);
static struct platform_driver cdns_salvo_phy_driver = {
.probe = cdns_salvo_phy_probe,
.driver = {
.name = "cdns-salvo-phy",
.of_match_table = cdns_salvo_phy_of_match,
}
};
module_platform_driver(cdns_salvo_phy_driver);
MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence SALVO PHY Driver");
|