xref: /linux/drivers/phy/cadence/phy-cadence-sierra.c (revision 380f57083c12936d6189fcda9e954ffcb821ec74)
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