398 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Salvo PHY is a 28nm PHY, it is a legacy PHY, and only4 * for USB3 and USB2.5 *6 * Copyright (c) 2019-2020 NXP7 */8 9#include <linux/bitfield.h>10#include <linux/clk.h>11#include <linux/io.h>12#include <linux/module.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/delay.h>16#include <linux/of.h>17#include <linux/of_platform.h>18 19#define USB3_PHY_OFFSET 0x020#define USB2_PHY_OFFSET 0x3800021/* USB3 PHY register definition */22#define PHY_PMA_CMN_CTRL1 0xC80023#define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e024#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x008425#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR 0x008526#define TB_ADDR_CMN_PLL0_INTDIV 0x009427#define TB_ADDR_CMN_PLL0_FRACDIV 0x009528#define TB_ADDR_CMN_PLL0_HIGH_THR 0x009629#define TB_ADDR_CMN_PLL0_SS_CTRL1 0x009830#define TB_ADDR_CMN_PLL0_SS_CTRL2 0x009931#define TB_ADDR_CMN_PLL0_DSM_DIAG 0x009732#define TB_ADDR_CMN_DIAG_PLL0_OVRD 0x01c233#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD 0x01c034#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD 0x01c135#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE 0x01C536#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE 0x01C637#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG 0x01C738#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE 0x01c439#define TB_ADDR_CMN_PSM_CLK_CTRL 0x006140#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR 0x40ea41#define TB_ADDR_XCVR_PSM_RCTRL 0x400142#define TB_ADDR_TX_PSC_A0 0x410043#define TB_ADDR_TX_PSC_A1 0x410144#define TB_ADDR_TX_PSC_A2 0x410245#define TB_ADDR_TX_PSC_A3 0x410346#define TB_ADDR_TX_DIAG_ECTRL_OVRD 0x41f547#define TB_ADDR_TX_PSC_CAL 0x410648#define TB_ADDR_TX_PSC_RDY 0x410749#define TB_ADDR_RX_PSC_A0 0x800050#define TB_ADDR_RX_PSC_A1 0x800151#define TB_ADDR_RX_PSC_A2 0x800252#define TB_ADDR_RX_PSC_A3 0x800353#define TB_ADDR_RX_PSC_CAL 0x800654#define TB_ADDR_RX_PSC_RDY 0x800755#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 0x405856#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY 0x41e757#define TB_ADDR_RX_SLC_CU_ITER_TMR 0x80e358#define TB_ADDR_RX_SIGDET_HL_FILT_TMR 0x809059#define TB_ADDR_RX_SAMP_DAC_CTRL 0x805860#define TB_ADDR_RX_DIAG_SIGDET_TUNE 0x81dc61#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 0x81df62#define TB_ADDR_RX_DIAG_BS_TM 0x81f563#define TB_ADDR_RX_DIAG_DFE_CTRL1 0x81d364#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 0x81c765#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 0x81c266#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 0x81c167#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 0x81c968#define TB_ADDR_RX_DIAG_RXFE_TM3 0x81f869#define TB_ADDR_RX_DIAG_RXFE_TM4 0x81f970#define TB_ADDR_RX_DIAG_LFPSDET_TUNE 0x81dd71#define TB_ADDR_RX_DIAG_DFE_CTRL3 0x81d572#define TB_ADDR_RX_DIAG_SC2C_DELAY 0x81e173#define TB_ADDR_RX_REE_VGA_GAIN_NODFE 0x81bf74#define TB_ADDR_XCVR_PSM_CAL_TMR 0x400275#define TB_ADDR_XCVR_PSM_A0BYP_TMR 0x400476#define TB_ADDR_XCVR_PSM_A0IN_TMR 0x400377#define TB_ADDR_XCVR_PSM_A1IN_TMR 0x400578#define TB_ADDR_XCVR_PSM_A2IN_TMR 0x400679#define TB_ADDR_XCVR_PSM_A3IN_TMR 0x400780#define TB_ADDR_XCVR_PSM_A4IN_TMR 0x400881#define TB_ADDR_XCVR_PSM_A5IN_TMR 0x400982#define TB_ADDR_XCVR_PSM_A0OUT_TMR 0x400a83#define TB_ADDR_XCVR_PSM_A1OUT_TMR 0x400b84#define TB_ADDR_XCVR_PSM_A2OUT_TMR 0x400c85#define TB_ADDR_XCVR_PSM_A3OUT_TMR 0x400d86#define TB_ADDR_XCVR_PSM_A4OUT_TMR 0x400e87#define TB_ADDR_XCVR_PSM_A5OUT_TMR 0x400f88#define TB_ADDR_TX_RCVDET_EN_TMR 0x412289#define TB_ADDR_TX_RCVDET_ST_TMR 0x412390#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f291#define TB_ADDR_TX_RCVDETSC_CTRL 0x412492 93/* USB2 PHY register definition */94#define UTMI_REG15 0xaf95#define UTMI_AFE_RX_REG0 0x0d96#define UTMI_AFE_RX_REG5 0x1297#define UTMI_AFE_BC_REG4 0x2998 99/* Align UTMI_AFE_RX_REG0 bit[7:6] define */100enum usb2_disconn_threshold {101 USB2_DISCONN_THRESHOLD_575 = 0x0,102 USB2_DISCONN_THRESHOLD_610 = 0x1,103 USB2_DISCONN_THRESHOLD_645 = 0x3,104};105 106#define RX_USB2_DISCONN_MASK GENMASK(7, 6)107 108/* TB_ADDR_TX_RCVDETSC_CTRL */109#define RXDET_IN_P3_32KHZ BIT(0)110/*111 * UTMI_REG15112 *113 * Gate how many us for the txvalid signal until analog114 * HS/FS transmitters have powered up115 */116#define TXVALID_GATE_THRESHOLD_HS_MASK (BIT(4) | BIT(5))117/* 0us, txvalid is ready just after HS/FS transmitters have powered up */118#define TXVALID_GATE_THRESHOLD_HS_0US (BIT(4) | BIT(5))119 120#define SET_B_SESSION_VALID (BIT(6) | BIT(5))121#define CLR_B_SESSION_VALID (BIT(6))122 123struct cdns_reg_pairs {124 u16 val;125 u32 off;126};127 128struct cdns_salvo_data {129 u8 reg_offset_shift;130 const struct cdns_reg_pairs *init_sequence_val;131 u8 init_sequence_length;132};133 134struct cdns_salvo_phy {135 struct phy *phy;136 struct clk *clk;137 void __iomem *base;138 struct cdns_salvo_data *data;139 enum usb2_disconn_threshold usb2_disconn;140};141 142static const struct of_device_id cdns_salvo_phy_of_match[];143static const struct cdns_salvo_data cdns_nxp_salvo_data;144 145static bool cdns_is_nxp_phy(struct cdns_salvo_phy *salvo_phy)146{147 return salvo_phy->data == &cdns_nxp_salvo_data;148}149 150static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 offset, u32 reg)151{152 return (u16)readl(salvo_phy->base + offset +153 reg * (1 << salvo_phy->data->reg_offset_shift));154}155 156static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, u32 offset,157 u32 reg, u16 val)158{159 writel(val, salvo_phy->base + offset +160 reg * (1 << salvo_phy->data->reg_offset_shift));161}162 163/*164 * Below bringup sequence pair are from Cadence PHY's User Guide165 * and NXP platform tuning results.166 */167static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {168 {0x0830, PHY_PMA_CMN_CTRL1},169 {0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL},170 {0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR},171 {0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR},172 {0x00d0, TB_ADDR_CMN_PLL0_INTDIV},173 {0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV},174 {0x0034, TB_ADDR_CMN_PLL0_HIGH_THR},175 {0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1},176 {0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2},177 {0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG},178 {0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD},179 {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD},180 {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD},181 {0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE},182 {0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE},183 {0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG},184 {0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE},185 {0x000a, TB_ADDR_CMN_PSM_CLK_CTRL},186 {0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR},187 {0xbefc, TB_ADDR_XCVR_PSM_RCTRL},188 189 {0x7799, TB_ADDR_TX_PSC_A0},190 {0x7798, TB_ADDR_TX_PSC_A1},191 {0x509b, TB_ADDR_TX_PSC_A2},192 {0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD},193 {0x509b, TB_ADDR_TX_PSC_A3},194 {0x2090, TB_ADDR_TX_PSC_CAL},195 {0x2090, TB_ADDR_TX_PSC_RDY},196 197 {0xA6FD, TB_ADDR_RX_PSC_A0},198 {0xA6FD, TB_ADDR_RX_PSC_A1},199 {0xA410, TB_ADDR_RX_PSC_A2},200 {0x2410, TB_ADDR_RX_PSC_A3},201 202 {0x23FF, TB_ADDR_RX_PSC_CAL},203 {0x2010, TB_ADDR_RX_PSC_RDY},204 205 {0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000},206 {0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY},207 {0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR},208 {0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR},209 {0x0000, TB_ADDR_RX_SAMP_DAC_CTRL},210 {0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE},211 {0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2},212 {0x0480, TB_ADDR_RX_DIAG_BS_TM},213 {0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1},214 {0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4},215 {0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0},216 {0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0},217 {0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6},218 {0x8000, TB_ADDR_RX_DIAG_RXFE_TM3},219 {0x0003, TB_ADDR_RX_DIAG_RXFE_TM4},220 {0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE},221 {0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3},222 {0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY},223 {0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE},224 225 {0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR},226 {0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR},227 {0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR},228 {0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR},229 {0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR},230 {0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR},231 {0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR},232 {0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR},233 234 {0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR},235 {0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR},236 {0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR},237 {0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR},238 {0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR},239 {0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR},240 /* Change rx detect parameter */241 {0x0960, TB_ADDR_TX_RCVDET_EN_TMR},242 {0x01e0, TB_ADDR_TX_RCVDET_ST_TMR},243 {0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR},244};245 246static int cdns_salvo_phy_init(struct phy *phy)247{248 struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);249 struct cdns_salvo_data *data = salvo_phy->data;250 int ret, i;251 u16 value;252 253 ret = clk_prepare_enable(salvo_phy->clk);254 if (ret)255 return ret;256 257 for (i = 0; i < data->init_sequence_length; i++) {258 const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;259 260 cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, reg_pair->off, reg_pair->val);261 }262 263 /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */264 value = cdns_salvo_read(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL);265 value |= RXDET_IN_P3_32KHZ;266 cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL,267 RXDET_IN_P3_32KHZ);268 269 value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15);270 value &= ~TXVALID_GATE_THRESHOLD_HS_MASK;271 cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15,272 value | TXVALID_GATE_THRESHOLD_HS_0US);273 274 cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG5, 0x5);275 276 value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0);277 value &= ~RX_USB2_DISCONN_MASK;278 value = FIELD_PREP(RX_USB2_DISCONN_MASK, salvo_phy->usb2_disconn);279 cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0, value);280 281 udelay(10);282 283 clk_disable_unprepare(salvo_phy->clk);284 285 return ret;286}287 288static int cdns_salvo_phy_power_on(struct phy *phy)289{290 struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);291 292 return clk_prepare_enable(salvo_phy->clk);293}294 295static int cdns_salvo_phy_power_off(struct phy *phy)296{297 struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);298 299 clk_disable_unprepare(salvo_phy->clk);300 301 return 0;302}303 304static int cdns_salvo_set_mode(struct phy *phy, enum phy_mode mode, int submode)305{306 struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);307 308 if (!cdns_is_nxp_phy(salvo_phy))309 return 0;310 311 if (mode == PHY_MODE_USB_DEVICE)312 cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4,313 SET_B_SESSION_VALID);314 else315 cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4,316 CLR_B_SESSION_VALID);317 318 return 0;319}320 321static const struct phy_ops cdns_salvo_phy_ops = {322 .init = cdns_salvo_phy_init,323 .power_on = cdns_salvo_phy_power_on,324 .power_off = cdns_salvo_phy_power_off,325 .owner = THIS_MODULE,326 .set_mode = cdns_salvo_set_mode,327};328 329static int cdns_salvo_phy_probe(struct platform_device *pdev)330{331 struct phy_provider *phy_provider;332 struct device *dev = &pdev->dev;333 struct cdns_salvo_phy *salvo_phy;334 struct cdns_salvo_data *data;335 u32 val;336 337 data = (struct cdns_salvo_data *)of_device_get_match_data(dev);338 salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL);339 if (!salvo_phy)340 return -ENOMEM;341 342 salvo_phy->data = data;343 salvo_phy->clk = devm_clk_get_optional(dev, "salvo_phy_clk");344 if (IS_ERR(salvo_phy->clk))345 return PTR_ERR(salvo_phy->clk);346 347 if (of_property_read_u32(dev->of_node, "cdns,usb2-disconnect-threshold-microvolt", &val))348 val = 575;349 350 if (val < 610)351 salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_575;352 else if (val < 645)353 salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_610;354 else355 salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_645;356 357 salvo_phy->base = devm_platform_ioremap_resource(pdev, 0);358 if (IS_ERR(salvo_phy->base))359 return PTR_ERR(salvo_phy->base);360 361 salvo_phy->phy = devm_phy_create(dev, NULL, &cdns_salvo_phy_ops);362 if (IS_ERR(salvo_phy->phy))363 return PTR_ERR(salvo_phy->phy);364 365 phy_set_drvdata(salvo_phy->phy, salvo_phy);366 367 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);368 return PTR_ERR_OR_ZERO(phy_provider);369}370 371static const struct cdns_salvo_data cdns_nxp_salvo_data = {372 2,373 cdns_nxp_sequence_pair,374 ARRAY_SIZE(cdns_nxp_sequence_pair),375};376 377static const struct of_device_id cdns_salvo_phy_of_match[] = {378 {379 .compatible = "nxp,salvo-phy",380 .data = &cdns_nxp_salvo_data,381 },382 {}383};384MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match);385 386static struct platform_driver cdns_salvo_phy_driver = {387 .probe = cdns_salvo_phy_probe,388 .driver = {389 .name = "cdns-salvo-phy",390 .of_match_table = cdns_salvo_phy_of_match,391 }392};393module_platform_driver(cdns_salvo_phy_driver);394 395MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");396MODULE_LICENSE("GPL v2");397MODULE_DESCRIPTION("Cadence SALVO PHY Driver");398