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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
|
// 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/bitfield.h>
#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>
#define USB3_PHY_OFFSET 0x0
#define USB2_PHY_OFFSET 0x38000
/* USB3 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
/* USB2 PHY register definition */
#define UTMI_REG15 0xaf
#define UTMI_AFE_RX_REG0 0x0d
#define UTMI_AFE_RX_REG5 0x12
#define UTMI_AFE_BC_REG4 0x29
/* Align UTMI_AFE_RX_REG0 bit[7:6] define */
enum usb2_disconn_threshold {
USB2_DISCONN_THRESHOLD_575 = 0x0,
USB2_DISCONN_THRESHOLD_610 = 0x1,
USB2_DISCONN_THRESHOLD_645 = 0x3,
};
#define RX_USB2_DISCONN_MASK GENMASK(7, 6)
/* TB_ADDR_TX_RCVDETSC_CTRL */
#define RXDET_IN_P3_32KHZ BIT(0)
/*
* UTMI_REG15
*
* Gate how many us for the txvalid signal until analog
* HS/FS transmitters have powered up
*/
#define TXVALID_GATE_THRESHOLD_HS_MASK (BIT(4) | BIT(5))
/* 0us, txvalid is ready just after HS/FS transmitters have powered up */
#define TXVALID_GATE_THRESHOLD_HS_0US (BIT(4) | BIT(5))
#define SET_B_SESSION_VALID (BIT(6) | BIT(5))
#define CLR_B_SESSION_VALID (BIT(6))
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;
enum usb2_disconn_threshold usb2_disconn;
};
static const struct of_device_id cdns_salvo_phy_of_match[];
static const struct cdns_salvo_data cdns_nxp_salvo_data;
static bool cdns_is_nxp_phy(struct cdns_salvo_phy *salvo_phy)
{
return salvo_phy->data == &cdns_nxp_salvo_data;
}
static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 offset, u32 reg)
{
return (u16)readl(salvo_phy->base + offset +
reg * (1 << salvo_phy->data->reg_offset_shift));
}
static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, u32 offset,
u32 reg, u16 val)
{
writel(val, salvo_phy->base + offset +
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, USB3_PHY_OFFSET, reg_pair->off, reg_pair->val);
}
/* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
value = cdns_salvo_read(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL);
value |= RXDET_IN_P3_32KHZ;
cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL,
RXDET_IN_P3_32KHZ);
value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15);
value &= ~TXVALID_GATE_THRESHOLD_HS_MASK;
cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15,
value | TXVALID_GATE_THRESHOLD_HS_0US);
cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG5, 0x5);
value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0);
value &= ~RX_USB2_DISCONN_MASK;
value = FIELD_PREP(RX_USB2_DISCONN_MASK, salvo_phy->usb2_disconn);
cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0, value);
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 int cdns_salvo_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
if (!cdns_is_nxp_phy(salvo_phy))
return 0;
if (mode == PHY_MODE_USB_DEVICE)
cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4,
SET_B_SESSION_VALID);
else
cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4,
CLR_B_SESSION_VALID);
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,
.set_mode = cdns_salvo_set_mode,
};
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 cdns_salvo_data *data;
u32 val;
data = (struct cdns_salvo_data *)of_device_get_match_data(dev);
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);
if (of_property_read_u32(dev->of_node, "cdns,usb2-disconnect-threshold-microvolt", &val))
val = 575;
if (val < 610)
salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_575;
else if (val < 645)
salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_610;
else
salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_645;
salvo_phy->base = devm_platform_ioremap_resource(pdev, 0);
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");
|