144d30d62SAlan Douglas // SPDX-License-Identifier: GPL-2.0 244d30d62SAlan Douglas /* 344d30d62SAlan Douglas * Cadence Sierra PHY Driver 444d30d62SAlan Douglas * 544d30d62SAlan Douglas * Copyright (c) 2018 Cadence Design Systems 644d30d62SAlan Douglas * Author: Alan Douglas <adouglas@cadence.com> 744d30d62SAlan Douglas * 844d30d62SAlan Douglas */ 944d30d62SAlan Douglas #include <linux/clk.h> 1044d30d62SAlan Douglas #include <linux/delay.h> 1144d30d62SAlan Douglas #include <linux/err.h> 1244d30d62SAlan Douglas #include <linux/io.h> 1344d30d62SAlan Douglas #include <linux/module.h> 1444d30d62SAlan Douglas #include <linux/phy/phy.h> 1544d30d62SAlan Douglas #include <linux/platform_device.h> 1644d30d62SAlan Douglas #include <linux/pm_runtime.h> 1744d30d62SAlan Douglas #include <linux/regmap.h> 1844d30d62SAlan Douglas #include <linux/reset.h> 1944d30d62SAlan Douglas #include <linux/slab.h> 2044d30d62SAlan Douglas #include <linux/of.h> 2144d30d62SAlan Douglas #include <linux/of_platform.h> 2244d30d62SAlan Douglas #include <dt-bindings/phy/phy.h> 2344d30d62SAlan Douglas 2444d30d62SAlan Douglas /* PHY register offsets */ 25*380f5708SKishon Vijay Abraham I #define SIERRA_COMMON_CDB_OFFSET 0x0 26*380f5708SKishon Vijay Abraham I #define SIERRA_MACRO_ID_REG 0x0 27*380f5708SKishon Vijay Abraham I 28*380f5708SKishon Vijay Abraham I #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ 29*380f5708SKishon Vijay Abraham I ((0x4000 << (block_offset)) + \ 30*380f5708SKishon Vijay Abraham I (((ln) << 9) << (reg_offset))) 31*380f5708SKishon Vijay Abraham I #define SIERRA_DET_STANDEC_A 0x000 32*380f5708SKishon Vijay Abraham I #define SIERRA_DET_STANDEC_B 0x001 33*380f5708SKishon Vijay Abraham I #define SIERRA_DET_STANDEC_C 0x002 34*380f5708SKishon Vijay Abraham I #define SIERRA_DET_STANDEC_D 0x003 35*380f5708SKishon Vijay Abraham I #define SIERRA_DET_STANDEC_E 0x004 36*380f5708SKishon Vijay Abraham I #define SIERRA_PSM_LANECAL 0x008 37*380f5708SKishon Vijay Abraham I #define SIERRA_PSM_DIAG 0x015 38*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_TX_A0 0x028 39*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_TX_A1 0x029 40*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_TX_A2 0x02A 41*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_TX_A3 0x02B 42*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_RX_A0 0x030 43*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_RX_A1 0x031 44*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_RX_A2 0x032 45*380f5708SKishon Vijay Abraham I #define SIERRA_PSC_RX_A3 0x033 46*380f5708SKishon Vijay Abraham I #define SIERRA_PLLCTRL_SUBRATE 0x03A 47*380f5708SKishon Vijay Abraham I #define SIERRA_PLLCTRL_GEN_D 0x03E 48*380f5708SKishon Vijay Abraham I #define SIERRA_DRVCTRL_ATTEN 0x06A 49*380f5708SKishon Vijay Abraham I #define SIERRA_CLKPATHCTRL_TMR 0x081 50*380f5708SKishon Vijay Abraham I #define SIERRA_RX_CREQ_FLTR_A_MODE1 0x087 51*380f5708SKishon Vijay Abraham I #define SIERRA_RX_CREQ_FLTR_A_MODE0 0x088 52*380f5708SKishon Vijay Abraham I #define SIERRA_CREQ_CCLKDET_MODE01 0x08E 53*380f5708SKishon Vijay Abraham I #define SIERRA_RX_CTLE_MAINTENANCE 0x091 54*380f5708SKishon Vijay Abraham I #define SIERRA_CREQ_FSMCLK_SEL 0x092 55*380f5708SKishon Vijay Abraham I #define SIERRA_CTLELUT_CTRL 0x098 56*380f5708SKishon Vijay Abraham I #define SIERRA_DFE_ECMP_RATESEL 0x0C0 57*380f5708SKishon Vijay Abraham I #define SIERRA_DFE_SMP_RATESEL 0x0C1 58*380f5708SKishon Vijay Abraham I #define SIERRA_DEQ_VGATUNE_CTRL 0x0E1 59*380f5708SKishon Vijay Abraham I #define SIERRA_TMRVAL_MODE3 0x16E 60*380f5708SKishon Vijay Abraham I #define SIERRA_TMRVAL_MODE2 0x16F 61*380f5708SKishon Vijay Abraham I #define SIERRA_TMRVAL_MODE1 0x170 62*380f5708SKishon Vijay Abraham I #define SIERRA_TMRVAL_MODE0 0x171 63*380f5708SKishon Vijay Abraham I #define SIERRA_PICNT_MODE1 0x174 64*380f5708SKishon Vijay Abraham I #define SIERRA_CPI_OUTBUF_RATESEL 0x17C 65*380f5708SKishon Vijay Abraham I #define SIERRA_LFPSFILT_NS 0x18A 66*380f5708SKishon Vijay Abraham I #define SIERRA_LFPSFILT_RD 0x18B 67*380f5708SKishon Vijay Abraham I #define SIERRA_LFPSFILT_MP 0x18C 68*380f5708SKishon Vijay Abraham I #define SIERRA_SDFILT_H2L_A 0x191 69*380f5708SKishon Vijay Abraham I 70*380f5708SKishon Vijay Abraham I #define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \ 71*380f5708SKishon Vijay Abraham I (0xc000 << (block_offset)) 72*380f5708SKishon Vijay Abraham I #define SIERRA_PHY_PLL_CFG 0xe 7344d30d62SAlan Douglas 7444d30d62SAlan Douglas #define SIERRA_MACRO_ID 0x00007364 7544d30d62SAlan Douglas #define SIERRA_MAX_LANES 4 7644d30d62SAlan Douglas 77*380f5708SKishon Vijay Abraham I static const struct reg_field macro_id_type = 78*380f5708SKishon Vijay Abraham I REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); 79*380f5708SKishon Vijay Abraham I static const struct reg_field phy_pll_cfg_1 = 80*380f5708SKishon Vijay Abraham I REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1); 81*380f5708SKishon Vijay Abraham I 8244d30d62SAlan Douglas struct cdns_sierra_inst { 8344d30d62SAlan Douglas struct phy *phy; 8444d30d62SAlan Douglas u32 phy_type; 8544d30d62SAlan Douglas u32 num_lanes; 8644d30d62SAlan Douglas u32 mlane; 8744d30d62SAlan Douglas struct reset_control *lnk_rst; 8844d30d62SAlan Douglas }; 8944d30d62SAlan Douglas 9044d30d62SAlan Douglas struct cdns_reg_pairs { 9144d30d62SAlan Douglas u16 val; 9244d30d62SAlan Douglas u32 off; 9344d30d62SAlan Douglas }; 9444d30d62SAlan Douglas 9544d30d62SAlan Douglas struct cdns_sierra_data { 9644d30d62SAlan Douglas u32 id_value; 97*380f5708SKishon Vijay Abraham I u8 block_offset_shift; 98*380f5708SKishon Vijay Abraham I u8 reg_offset_shift; 9944d30d62SAlan Douglas u32 pcie_regs; 10044d30d62SAlan Douglas u32 usb_regs; 10144d30d62SAlan Douglas struct cdns_reg_pairs *pcie_vals; 10244d30d62SAlan Douglas struct cdns_reg_pairs *usb_vals; 10344d30d62SAlan Douglas }; 10444d30d62SAlan Douglas 105*380f5708SKishon Vijay Abraham I struct cdns_regmap_cdb_context { 10644d30d62SAlan Douglas struct device *dev; 10744d30d62SAlan Douglas void __iomem *base; 108*380f5708SKishon Vijay Abraham I u8 reg_offset_shift; 109*380f5708SKishon Vijay Abraham I }; 110*380f5708SKishon Vijay Abraham I 111*380f5708SKishon Vijay Abraham I struct cdns_sierra_phy { 112*380f5708SKishon Vijay Abraham I struct device *dev; 113*380f5708SKishon Vijay Abraham I struct regmap *regmap; 11444d30d62SAlan Douglas struct cdns_sierra_data *init_data; 11544d30d62SAlan Douglas struct cdns_sierra_inst phys[SIERRA_MAX_LANES]; 11644d30d62SAlan Douglas struct reset_control *phy_rst; 11744d30d62SAlan Douglas struct reset_control *apb_rst; 118*380f5708SKishon Vijay Abraham I struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES]; 119*380f5708SKishon Vijay Abraham I struct regmap *regmap_phy_config_ctrl; 120*380f5708SKishon Vijay Abraham I struct regmap *regmap_common_cdb; 121*380f5708SKishon Vijay Abraham I struct regmap_field *macro_id_type; 122*380f5708SKishon Vijay Abraham I struct regmap_field *phy_pll_cfg_1; 12344d30d62SAlan Douglas struct clk *clk; 12444d30d62SAlan Douglas int nsubnodes; 12544d30d62SAlan Douglas bool autoconf; 12644d30d62SAlan Douglas }; 12744d30d62SAlan Douglas 128*380f5708SKishon Vijay Abraham I static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val) 129*380f5708SKishon Vijay Abraham I { 130*380f5708SKishon Vijay Abraham I struct cdns_regmap_cdb_context *ctx = context; 131*380f5708SKishon Vijay Abraham I u32 offset = reg << ctx->reg_offset_shift; 132*380f5708SKishon Vijay Abraham I 133*380f5708SKishon Vijay Abraham I writew(val, ctx->base + offset); 134*380f5708SKishon Vijay Abraham I 135*380f5708SKishon Vijay Abraham I return 0; 136*380f5708SKishon Vijay Abraham I } 137*380f5708SKishon Vijay Abraham I 138*380f5708SKishon Vijay Abraham I static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val) 139*380f5708SKishon Vijay Abraham I { 140*380f5708SKishon Vijay Abraham I struct cdns_regmap_cdb_context *ctx = context; 141*380f5708SKishon Vijay Abraham I u32 offset = reg << ctx->reg_offset_shift; 142*380f5708SKishon Vijay Abraham I 143*380f5708SKishon Vijay Abraham I *val = readw(ctx->base + offset); 144*380f5708SKishon Vijay Abraham I return 0; 145*380f5708SKishon Vijay Abraham I } 146*380f5708SKishon Vijay Abraham I 147*380f5708SKishon Vijay Abraham I #define SIERRA_LANE_CDB_REGMAP_CONF(n) \ 148*380f5708SKishon Vijay Abraham I { \ 149*380f5708SKishon Vijay Abraham I .name = "sierra_lane" n "_cdb", \ 150*380f5708SKishon Vijay Abraham I .reg_stride = 1, \ 151*380f5708SKishon Vijay Abraham I .fast_io = true, \ 152*380f5708SKishon Vijay Abraham I .reg_write = cdns_regmap_write, \ 153*380f5708SKishon Vijay Abraham I .reg_read = cdns_regmap_read, \ 154*380f5708SKishon Vijay Abraham I } 155*380f5708SKishon Vijay Abraham I 156*380f5708SKishon Vijay Abraham I static struct regmap_config cdns_sierra_lane_cdb_config[] = { 157*380f5708SKishon Vijay Abraham I SIERRA_LANE_CDB_REGMAP_CONF("0"), 158*380f5708SKishon Vijay Abraham I SIERRA_LANE_CDB_REGMAP_CONF("1"), 159*380f5708SKishon Vijay Abraham I SIERRA_LANE_CDB_REGMAP_CONF("2"), 160*380f5708SKishon Vijay Abraham I SIERRA_LANE_CDB_REGMAP_CONF("3"), 161*380f5708SKishon Vijay Abraham I }; 162*380f5708SKishon Vijay Abraham I 163*380f5708SKishon Vijay Abraham I static struct regmap_config cdns_sierra_common_cdb_config = { 164*380f5708SKishon Vijay Abraham I .name = "sierra_common_cdb", 165*380f5708SKishon Vijay Abraham I .reg_stride = 1, 166*380f5708SKishon Vijay Abraham I .fast_io = true, 167*380f5708SKishon Vijay Abraham I .reg_write = cdns_regmap_write, 168*380f5708SKishon Vijay Abraham I .reg_read = cdns_regmap_read, 169*380f5708SKishon Vijay Abraham I }; 170*380f5708SKishon Vijay Abraham I 171*380f5708SKishon Vijay Abraham I static struct regmap_config cdns_sierra_phy_config_ctrl_config = { 172*380f5708SKishon Vijay Abraham I .name = "sierra_phy_config_ctrl", 173*380f5708SKishon Vijay Abraham I .reg_stride = 1, 174*380f5708SKishon Vijay Abraham I .fast_io = true, 175*380f5708SKishon Vijay Abraham I .reg_write = cdns_regmap_write, 176*380f5708SKishon Vijay Abraham I .reg_read = cdns_regmap_read, 177*380f5708SKishon Vijay Abraham I }; 178*380f5708SKishon Vijay Abraham I 17944d30d62SAlan Douglas static void cdns_sierra_phy_init(struct phy *gphy) 18044d30d62SAlan Douglas { 18144d30d62SAlan Douglas struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); 18244d30d62SAlan Douglas struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); 183*380f5708SKishon Vijay Abraham I struct regmap *regmap = phy->regmap; 18444d30d62SAlan Douglas int i, j; 18544d30d62SAlan Douglas struct cdns_reg_pairs *vals; 18644d30d62SAlan Douglas u32 num_regs; 18744d30d62SAlan Douglas 18844d30d62SAlan Douglas if (ins->phy_type == PHY_TYPE_PCIE) { 18944d30d62SAlan Douglas num_regs = phy->init_data->pcie_regs; 19044d30d62SAlan Douglas vals = phy->init_data->pcie_vals; 19144d30d62SAlan Douglas } else if (ins->phy_type == PHY_TYPE_USB3) { 19244d30d62SAlan Douglas num_regs = phy->init_data->usb_regs; 19344d30d62SAlan Douglas vals = phy->init_data->usb_vals; 19444d30d62SAlan Douglas } else { 19544d30d62SAlan Douglas return; 19644d30d62SAlan Douglas } 197*380f5708SKishon Vijay Abraham I for (i = 0; i < ins->num_lanes; i++) { 198*380f5708SKishon Vijay Abraham I for (j = 0; j < num_regs ; j++) { 199*380f5708SKishon Vijay Abraham I regmap = phy->regmap_lane_cdb[i + ins->mlane]; 200*380f5708SKishon Vijay Abraham I regmap_write(regmap, vals[j].off, vals[j].val); 201*380f5708SKishon Vijay Abraham I } 202*380f5708SKishon Vijay Abraham I } 20344d30d62SAlan Douglas } 20444d30d62SAlan Douglas 20544d30d62SAlan Douglas static int cdns_sierra_phy_on(struct phy *gphy) 20644d30d62SAlan Douglas { 20744d30d62SAlan Douglas struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); 20844d30d62SAlan Douglas 20944d30d62SAlan Douglas /* Take the PHY lane group out of reset */ 21044d30d62SAlan Douglas return reset_control_deassert(ins->lnk_rst); 21144d30d62SAlan Douglas } 21244d30d62SAlan Douglas 21344d30d62SAlan Douglas static int cdns_sierra_phy_off(struct phy *gphy) 21444d30d62SAlan Douglas { 21544d30d62SAlan Douglas struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); 21644d30d62SAlan Douglas 21744d30d62SAlan Douglas return reset_control_assert(ins->lnk_rst); 21844d30d62SAlan Douglas } 21944d30d62SAlan Douglas 22044d30d62SAlan Douglas static const struct phy_ops ops = { 22144d30d62SAlan Douglas .power_on = cdns_sierra_phy_on, 22244d30d62SAlan Douglas .power_off = cdns_sierra_phy_off, 22344d30d62SAlan Douglas .owner = THIS_MODULE, 22444d30d62SAlan Douglas }; 22544d30d62SAlan Douglas 22644d30d62SAlan Douglas static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, 22744d30d62SAlan Douglas struct device_node *child) 22844d30d62SAlan Douglas { 22944d30d62SAlan Douglas if (of_property_read_u32(child, "reg", &inst->mlane)) 23044d30d62SAlan Douglas return -EINVAL; 23144d30d62SAlan Douglas 23244d30d62SAlan Douglas if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes)) 23344d30d62SAlan Douglas return -EINVAL; 23444d30d62SAlan Douglas 23544d30d62SAlan Douglas if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type)) 23644d30d62SAlan Douglas return -EINVAL; 23744d30d62SAlan Douglas 23844d30d62SAlan Douglas return 0; 23944d30d62SAlan Douglas } 24044d30d62SAlan Douglas 24144d30d62SAlan Douglas static const struct of_device_id cdns_sierra_id_table[]; 24244d30d62SAlan Douglas 243*380f5708SKishon Vijay Abraham I static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, 244*380f5708SKishon Vijay Abraham I u32 block_offset, u8 reg_offset_shift, 245*380f5708SKishon Vijay Abraham I const struct regmap_config *config) 246*380f5708SKishon Vijay Abraham I { 247*380f5708SKishon Vijay Abraham I struct cdns_regmap_cdb_context *ctx; 248*380f5708SKishon Vijay Abraham I 249*380f5708SKishon Vijay Abraham I ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 250*380f5708SKishon Vijay Abraham I if (!ctx) 251*380f5708SKishon Vijay Abraham I return ERR_PTR(-ENOMEM); 252*380f5708SKishon Vijay Abraham I 253*380f5708SKishon Vijay Abraham I ctx->dev = dev; 254*380f5708SKishon Vijay Abraham I ctx->base = base + block_offset; 255*380f5708SKishon Vijay Abraham I ctx->reg_offset_shift = reg_offset_shift; 256*380f5708SKishon Vijay Abraham I 257*380f5708SKishon Vijay Abraham I return devm_regmap_init(dev, NULL, ctx, config); 258*380f5708SKishon Vijay Abraham I } 259*380f5708SKishon Vijay Abraham I 260*380f5708SKishon Vijay Abraham I static int cdns_regfield_init(struct cdns_sierra_phy *sp) 261*380f5708SKishon Vijay Abraham I { 262*380f5708SKishon Vijay Abraham I struct device *dev = sp->dev; 263*380f5708SKishon Vijay Abraham I struct regmap_field *field; 264*380f5708SKishon Vijay Abraham I struct regmap *regmap; 265*380f5708SKishon Vijay Abraham I 266*380f5708SKishon Vijay Abraham I regmap = sp->regmap_common_cdb; 267*380f5708SKishon Vijay Abraham I field = devm_regmap_field_alloc(dev, regmap, macro_id_type); 268*380f5708SKishon Vijay Abraham I if (IS_ERR(field)) { 269*380f5708SKishon Vijay Abraham I dev_err(dev, "MACRO_ID_TYPE reg field init failed\n"); 270*380f5708SKishon Vijay Abraham I return PTR_ERR(field); 271*380f5708SKishon Vijay Abraham I } 272*380f5708SKishon Vijay Abraham I sp->macro_id_type = field; 273*380f5708SKishon Vijay Abraham I 274*380f5708SKishon Vijay Abraham I regmap = sp->regmap_phy_config_ctrl; 275*380f5708SKishon Vijay Abraham I field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1); 276*380f5708SKishon Vijay Abraham I if (IS_ERR(field)) { 277*380f5708SKishon Vijay Abraham I dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n"); 278*380f5708SKishon Vijay Abraham I return PTR_ERR(field); 279*380f5708SKishon Vijay Abraham I } 280*380f5708SKishon Vijay Abraham I sp->phy_pll_cfg_1 = field; 281*380f5708SKishon Vijay Abraham I 282*380f5708SKishon Vijay Abraham I return 0; 283*380f5708SKishon Vijay Abraham I } 284*380f5708SKishon Vijay Abraham I 285*380f5708SKishon Vijay Abraham I static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, 286*380f5708SKishon Vijay Abraham I void __iomem *base, u8 block_offset_shift, 287*380f5708SKishon Vijay Abraham I u8 reg_offset_shift) 288*380f5708SKishon Vijay Abraham I { 289*380f5708SKishon Vijay Abraham I struct device *dev = sp->dev; 290*380f5708SKishon Vijay Abraham I struct regmap *regmap; 291*380f5708SKishon Vijay Abraham I u32 block_offset; 292*380f5708SKishon Vijay Abraham I int i; 293*380f5708SKishon Vijay Abraham I 294*380f5708SKishon Vijay Abraham I for (i = 0; i < SIERRA_MAX_LANES; i++) { 295*380f5708SKishon Vijay Abraham I block_offset = SIERRA_LANE_CDB_OFFSET(i, block_offset_shift, 296*380f5708SKishon Vijay Abraham I reg_offset_shift); 297*380f5708SKishon Vijay Abraham I regmap = cdns_regmap_init(dev, base, block_offset, 298*380f5708SKishon Vijay Abraham I reg_offset_shift, 299*380f5708SKishon Vijay Abraham I &cdns_sierra_lane_cdb_config[i]); 300*380f5708SKishon Vijay Abraham I if (IS_ERR(regmap)) { 301*380f5708SKishon Vijay Abraham I dev_err(dev, "Failed to init lane CDB regmap\n"); 302*380f5708SKishon Vijay Abraham I return PTR_ERR(regmap); 303*380f5708SKishon Vijay Abraham I } 304*380f5708SKishon Vijay Abraham I sp->regmap_lane_cdb[i] = regmap; 305*380f5708SKishon Vijay Abraham I } 306*380f5708SKishon Vijay Abraham I 307*380f5708SKishon Vijay Abraham I regmap = cdns_regmap_init(dev, base, SIERRA_COMMON_CDB_OFFSET, 308*380f5708SKishon Vijay Abraham I reg_offset_shift, 309*380f5708SKishon Vijay Abraham I &cdns_sierra_common_cdb_config); 310*380f5708SKishon Vijay Abraham I if (IS_ERR(regmap)) { 311*380f5708SKishon Vijay Abraham I dev_err(dev, "Failed to init common CDB regmap\n"); 312*380f5708SKishon Vijay Abraham I return PTR_ERR(regmap); 313*380f5708SKishon Vijay Abraham I } 314*380f5708SKishon Vijay Abraham I sp->regmap_common_cdb = regmap; 315*380f5708SKishon Vijay Abraham I 316*380f5708SKishon Vijay Abraham I block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift); 317*380f5708SKishon Vijay Abraham I regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, 318*380f5708SKishon Vijay Abraham I &cdns_sierra_phy_config_ctrl_config); 319*380f5708SKishon Vijay Abraham I if (IS_ERR(regmap)) { 320*380f5708SKishon Vijay Abraham I dev_err(dev, "Failed to init PHY config and control regmap\n"); 321*380f5708SKishon Vijay Abraham I return PTR_ERR(regmap); 322*380f5708SKishon Vijay Abraham I } 323*380f5708SKishon Vijay Abraham I sp->regmap_phy_config_ctrl = regmap; 324*380f5708SKishon Vijay Abraham I 325*380f5708SKishon Vijay Abraham I return 0; 326*380f5708SKishon Vijay Abraham I } 327*380f5708SKishon Vijay Abraham I 32844d30d62SAlan Douglas static int cdns_sierra_phy_probe(struct platform_device *pdev) 32944d30d62SAlan Douglas { 33044d30d62SAlan Douglas struct cdns_sierra_phy *sp; 33144d30d62SAlan Douglas struct phy_provider *phy_provider; 33244d30d62SAlan Douglas struct device *dev = &pdev->dev; 33344d30d62SAlan Douglas const struct of_device_id *match; 334*380f5708SKishon Vijay Abraham I struct cdns_sierra_data *data; 335*380f5708SKishon Vijay Abraham I unsigned int id_value; 33644d30d62SAlan Douglas struct resource *res; 33744d30d62SAlan Douglas int i, ret, node = 0; 338*380f5708SKishon Vijay Abraham I void __iomem *base; 33944d30d62SAlan Douglas struct device_node *dn = dev->of_node, *child; 34044d30d62SAlan Douglas 34144d30d62SAlan Douglas if (of_get_child_count(dn) == 0) 34244d30d62SAlan Douglas return -ENODEV; 34344d30d62SAlan Douglas 344*380f5708SKishon Vijay Abraham I /* Get init data for this PHY */ 345*380f5708SKishon Vijay Abraham I match = of_match_device(cdns_sierra_id_table, dev); 346*380f5708SKishon Vijay Abraham I if (!match) 347*380f5708SKishon Vijay Abraham I return -EINVAL; 348*380f5708SKishon Vijay Abraham I 349*380f5708SKishon Vijay Abraham I data = (struct cdns_sierra_data *)match->data; 350*380f5708SKishon Vijay Abraham I 35144d30d62SAlan Douglas sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); 35244d30d62SAlan Douglas if (!sp) 35344d30d62SAlan Douglas return -ENOMEM; 35444d30d62SAlan Douglas dev_set_drvdata(dev, sp); 35544d30d62SAlan Douglas sp->dev = dev; 356*380f5708SKishon Vijay Abraham I sp->init_data = data; 35744d30d62SAlan Douglas 35844d30d62SAlan Douglas res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 359*380f5708SKishon Vijay Abraham I base = devm_ioremap_resource(dev, res); 360*380f5708SKishon Vijay Abraham I if (IS_ERR(base)) { 36144d30d62SAlan Douglas dev_err(dev, "missing \"reg\"\n"); 362*380f5708SKishon Vijay Abraham I return PTR_ERR(base); 36344d30d62SAlan Douglas } 36444d30d62SAlan Douglas 365*380f5708SKishon Vijay Abraham I ret = cdns_regmap_init_blocks(sp, base, data->block_offset_shift, 366*380f5708SKishon Vijay Abraham I data->reg_offset_shift); 367*380f5708SKishon Vijay Abraham I if (ret) 368*380f5708SKishon Vijay Abraham I return ret; 369*380f5708SKishon Vijay Abraham I 370*380f5708SKishon Vijay Abraham I ret = cdns_regfield_init(sp); 371*380f5708SKishon Vijay Abraham I if (ret) 372*380f5708SKishon Vijay Abraham I return ret; 37344d30d62SAlan Douglas 37444d30d62SAlan Douglas platform_set_drvdata(pdev, sp); 37544d30d62SAlan Douglas 376372428dbSKishon Vijay Abraham I sp->clk = devm_clk_get_optional(dev, "phy_clk"); 37744d30d62SAlan Douglas if (IS_ERR(sp->clk)) { 37844d30d62SAlan Douglas dev_err(dev, "failed to get clock phy_clk\n"); 37944d30d62SAlan Douglas return PTR_ERR(sp->clk); 38044d30d62SAlan Douglas } 38144d30d62SAlan Douglas 38244d30d62SAlan Douglas sp->phy_rst = devm_reset_control_get(dev, "sierra_reset"); 38344d30d62SAlan Douglas if (IS_ERR(sp->phy_rst)) { 38444d30d62SAlan Douglas dev_err(dev, "failed to get reset\n"); 38544d30d62SAlan Douglas return PTR_ERR(sp->phy_rst); 38644d30d62SAlan Douglas } 38744d30d62SAlan Douglas 388372428dbSKishon Vijay Abraham I sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb"); 38944d30d62SAlan Douglas if (IS_ERR(sp->apb_rst)) { 39044d30d62SAlan Douglas dev_err(dev, "failed to get apb reset\n"); 39144d30d62SAlan Douglas return PTR_ERR(sp->apb_rst); 39244d30d62SAlan Douglas } 39344d30d62SAlan Douglas 39444d30d62SAlan Douglas ret = clk_prepare_enable(sp->clk); 39544d30d62SAlan Douglas if (ret) 39644d30d62SAlan Douglas return ret; 39744d30d62SAlan Douglas 39844d30d62SAlan Douglas /* Enable APB */ 39944d30d62SAlan Douglas reset_control_deassert(sp->apb_rst); 40044d30d62SAlan Douglas 40144d30d62SAlan Douglas /* Check that PHY is present */ 402*380f5708SKishon Vijay Abraham I regmap_field_read(sp->macro_id_type, &id_value); 403*380f5708SKishon Vijay Abraham I if (sp->init_data->id_value != id_value) { 40444d30d62SAlan Douglas ret = -EINVAL; 40544d30d62SAlan Douglas goto clk_disable; 40644d30d62SAlan Douglas } 40744d30d62SAlan Douglas 40844d30d62SAlan Douglas sp->autoconf = of_property_read_bool(dn, "cdns,autoconf"); 40944d30d62SAlan Douglas 41044d30d62SAlan Douglas for_each_available_child_of_node(dn, child) { 41144d30d62SAlan Douglas struct phy *gphy; 41244d30d62SAlan Douglas 41344d30d62SAlan Douglas sp->phys[node].lnk_rst = 41444d30d62SAlan Douglas of_reset_control_get_exclusive_by_index(child, 0); 41544d30d62SAlan Douglas 41644d30d62SAlan Douglas if (IS_ERR(sp->phys[node].lnk_rst)) { 41744d30d62SAlan Douglas dev_err(dev, "failed to get reset %s\n", 41844d30d62SAlan Douglas child->full_name); 41944d30d62SAlan Douglas ret = PTR_ERR(sp->phys[node].lnk_rst); 42044d30d62SAlan Douglas goto put_child2; 42144d30d62SAlan Douglas } 42244d30d62SAlan Douglas 42344d30d62SAlan Douglas if (!sp->autoconf) { 42444d30d62SAlan Douglas ret = cdns_sierra_get_optional(&sp->phys[node], child); 42544d30d62SAlan Douglas if (ret) { 42644d30d62SAlan Douglas dev_err(dev, "missing property in node %s\n", 42744d30d62SAlan Douglas child->name); 42844d30d62SAlan Douglas goto put_child; 42944d30d62SAlan Douglas } 43044d30d62SAlan Douglas } 43144d30d62SAlan Douglas 43244d30d62SAlan Douglas gphy = devm_phy_create(dev, child, &ops); 43344d30d62SAlan Douglas 43444d30d62SAlan Douglas if (IS_ERR(gphy)) { 43544d30d62SAlan Douglas ret = PTR_ERR(gphy); 43644d30d62SAlan Douglas goto put_child; 43744d30d62SAlan Douglas } 43844d30d62SAlan Douglas sp->phys[node].phy = gphy; 43944d30d62SAlan Douglas phy_set_drvdata(gphy, &sp->phys[node]); 44044d30d62SAlan Douglas 44144d30d62SAlan Douglas /* Initialise the PHY registers, unless auto configured */ 44244d30d62SAlan Douglas if (!sp->autoconf) 44344d30d62SAlan Douglas cdns_sierra_phy_init(gphy); 44444d30d62SAlan Douglas 44544d30d62SAlan Douglas node++; 44644d30d62SAlan Douglas } 44744d30d62SAlan Douglas sp->nsubnodes = node; 44844d30d62SAlan Douglas 44944d30d62SAlan Douglas /* If more than one subnode, configure the PHY as multilink */ 45044d30d62SAlan Douglas if (!sp->autoconf && sp->nsubnodes > 1) 451*380f5708SKishon Vijay Abraham I regmap_field_write(sp->phy_pll_cfg_1, 0x1); 45244d30d62SAlan Douglas 45344d30d62SAlan Douglas pm_runtime_enable(dev); 45444d30d62SAlan Douglas phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 45544d30d62SAlan Douglas reset_control_deassert(sp->phy_rst); 45644d30d62SAlan Douglas return PTR_ERR_OR_ZERO(phy_provider); 45744d30d62SAlan Douglas 45844d30d62SAlan Douglas put_child: 45944d30d62SAlan Douglas node++; 46044d30d62SAlan Douglas put_child2: 46144d30d62SAlan Douglas for (i = 0; i < node; i++) 46244d30d62SAlan Douglas reset_control_put(sp->phys[i].lnk_rst); 46344d30d62SAlan Douglas of_node_put(child); 46444d30d62SAlan Douglas clk_disable: 46544d30d62SAlan Douglas clk_disable_unprepare(sp->clk); 46644d30d62SAlan Douglas reset_control_assert(sp->apb_rst); 46744d30d62SAlan Douglas return ret; 46844d30d62SAlan Douglas } 46944d30d62SAlan Douglas 47044d30d62SAlan Douglas static int cdns_sierra_phy_remove(struct platform_device *pdev) 47144d30d62SAlan Douglas { 47244d30d62SAlan Douglas struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent); 47344d30d62SAlan Douglas int i; 47444d30d62SAlan Douglas 47544d30d62SAlan Douglas reset_control_assert(phy->phy_rst); 47644d30d62SAlan Douglas reset_control_assert(phy->apb_rst); 47744d30d62SAlan Douglas pm_runtime_disable(&pdev->dev); 47844d30d62SAlan Douglas 47944d30d62SAlan Douglas /* 48044d30d62SAlan Douglas * The device level resets will be put automatically. 48144d30d62SAlan Douglas * Need to put the subnode resets here though. 48244d30d62SAlan Douglas */ 48344d30d62SAlan Douglas for (i = 0; i < phy->nsubnodes; i++) { 48444d30d62SAlan Douglas reset_control_assert(phy->phys[i].lnk_rst); 48544d30d62SAlan Douglas reset_control_put(phy->phys[i].lnk_rst); 48644d30d62SAlan Douglas } 48744d30d62SAlan Douglas return 0; 48844d30d62SAlan Douglas } 48944d30d62SAlan Douglas 49044d30d62SAlan Douglas static struct cdns_reg_pairs cdns_usb_regs[] = { 49144d30d62SAlan Douglas /* 49244d30d62SAlan Douglas * Write USB configuration parameters to the PHY. 49344d30d62SAlan Douglas * These values are specific to this specific hardware 49444d30d62SAlan Douglas * configuration. 49544d30d62SAlan Douglas */ 49644d30d62SAlan Douglas {0xFE0A, SIERRA_DET_STANDEC_A}, 49744d30d62SAlan Douglas {0x000F, SIERRA_DET_STANDEC_B}, 49844d30d62SAlan Douglas {0x55A5, SIERRA_DET_STANDEC_C}, 49944d30d62SAlan Douglas {0x69AD, SIERRA_DET_STANDEC_D}, 50044d30d62SAlan Douglas {0x0241, SIERRA_DET_STANDEC_E}, 50144d30d62SAlan Douglas {0x0110, SIERRA_PSM_LANECAL}, 50244d30d62SAlan Douglas {0xCF00, SIERRA_PSM_DIAG}, 50344d30d62SAlan Douglas {0x001F, SIERRA_PSC_TX_A0}, 50444d30d62SAlan Douglas {0x0007, SIERRA_PSC_TX_A1}, 50544d30d62SAlan Douglas {0x0003, SIERRA_PSC_TX_A2}, 50644d30d62SAlan Douglas {0x0003, SIERRA_PSC_TX_A3}, 50744d30d62SAlan Douglas {0x0FFF, SIERRA_PSC_RX_A0}, 50844d30d62SAlan Douglas {0x0003, SIERRA_PSC_RX_A1}, 50944d30d62SAlan Douglas {0x0003, SIERRA_PSC_RX_A2}, 51044d30d62SAlan Douglas {0x0001, SIERRA_PSC_RX_A3}, 51144d30d62SAlan Douglas {0x0001, SIERRA_PLLCTRL_SUBRATE}, 51244d30d62SAlan Douglas {0x0406, SIERRA_PLLCTRL_GEN_D}, 51344d30d62SAlan Douglas {0x0000, SIERRA_DRVCTRL_ATTEN}, 51444d30d62SAlan Douglas {0x823E, SIERRA_CLKPATHCTRL_TMR}, 51544d30d62SAlan Douglas {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1}, 51644d30d62SAlan Douglas {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0}, 51744d30d62SAlan Douglas {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01}, 51844d30d62SAlan Douglas {0x023C, SIERRA_RX_CTLE_MAINTENANCE}, 51944d30d62SAlan Douglas {0x3232, SIERRA_CREQ_FSMCLK_SEL}, 52044d30d62SAlan Douglas {0x8452, SIERRA_CTLELUT_CTRL}, 52144d30d62SAlan Douglas {0x4121, SIERRA_DFE_ECMP_RATESEL}, 52244d30d62SAlan Douglas {0x4121, SIERRA_DFE_SMP_RATESEL}, 52344d30d62SAlan Douglas {0x9999, SIERRA_DEQ_VGATUNE_CTRL}, 52444d30d62SAlan Douglas {0x0330, SIERRA_TMRVAL_MODE0}, 52544d30d62SAlan Douglas {0x01FF, SIERRA_PICNT_MODE1}, 52644d30d62SAlan Douglas {0x0009, SIERRA_CPI_OUTBUF_RATESEL}, 52744d30d62SAlan Douglas {0x000F, SIERRA_LFPSFILT_NS}, 52844d30d62SAlan Douglas {0x0009, SIERRA_LFPSFILT_RD}, 52944d30d62SAlan Douglas {0x0001, SIERRA_LFPSFILT_MP}, 53044d30d62SAlan Douglas {0x8013, SIERRA_SDFILT_H2L_A}, 53144d30d62SAlan Douglas {0x0400, SIERRA_TMRVAL_MODE1}, 53244d30d62SAlan Douglas }; 53344d30d62SAlan Douglas 53444d30d62SAlan Douglas static struct cdns_reg_pairs cdns_pcie_regs[] = { 53544d30d62SAlan Douglas /* 53644d30d62SAlan Douglas * Write PCIe configuration parameters to the PHY. 53744d30d62SAlan Douglas * These values are specific to this specific hardware 53844d30d62SAlan Douglas * configuration. 53944d30d62SAlan Douglas */ 54044d30d62SAlan Douglas {0x891f, SIERRA_DET_STANDEC_D}, 54144d30d62SAlan Douglas {0x0053, SIERRA_DET_STANDEC_E}, 54244d30d62SAlan Douglas {0x0400, SIERRA_TMRVAL_MODE2}, 54344d30d62SAlan Douglas {0x0200, SIERRA_TMRVAL_MODE3}, 54444d30d62SAlan Douglas }; 54544d30d62SAlan Douglas 54644d30d62SAlan Douglas static const struct cdns_sierra_data cdns_map_sierra = { 54744d30d62SAlan Douglas SIERRA_MACRO_ID, 548*380f5708SKishon Vijay Abraham I 0x2, 549*380f5708SKishon Vijay Abraham I 0x2, 55044d30d62SAlan Douglas ARRAY_SIZE(cdns_pcie_regs), 55144d30d62SAlan Douglas ARRAY_SIZE(cdns_usb_regs), 55244d30d62SAlan Douglas cdns_pcie_regs, 55344d30d62SAlan Douglas cdns_usb_regs 55444d30d62SAlan Douglas }; 55544d30d62SAlan Douglas 55644d30d62SAlan Douglas static const struct of_device_id cdns_sierra_id_table[] = { 55744d30d62SAlan Douglas { 55844d30d62SAlan Douglas .compatible = "cdns,sierra-phy-t0", 55944d30d62SAlan Douglas .data = &cdns_map_sierra, 56044d30d62SAlan Douglas }, 56144d30d62SAlan Douglas {} 56244d30d62SAlan Douglas }; 56344d30d62SAlan Douglas MODULE_DEVICE_TABLE(of, cdns_sierra_id_table); 56444d30d62SAlan Douglas 56544d30d62SAlan Douglas static struct platform_driver cdns_sierra_driver = { 56644d30d62SAlan Douglas .probe = cdns_sierra_phy_probe, 56744d30d62SAlan Douglas .remove = cdns_sierra_phy_remove, 56844d30d62SAlan Douglas .driver = { 56944d30d62SAlan Douglas .name = "cdns-sierra-phy", 57044d30d62SAlan Douglas .of_match_table = cdns_sierra_id_table, 57144d30d62SAlan Douglas }, 57244d30d62SAlan Douglas }; 57344d30d62SAlan Douglas module_platform_driver(cdns_sierra_driver); 57444d30d62SAlan Douglas 57544d30d62SAlan Douglas MODULE_ALIAS("platform:cdns_sierra"); 57644d30d62SAlan Douglas MODULE_AUTHOR("Cadence Design Systems"); 57744d30d62SAlan Douglas MODULE_DESCRIPTION("CDNS sierra phy driver"); 57844d30d62SAlan Douglas MODULE_LICENSE("GPL v2"); 579