1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Salvo PHY is a 28nm PHY, it is a legacy PHY, and only |
4 | * for USB3 and USB2. |
5 | * |
6 | * Copyright (c) 2019-2020 NXP |
7 | */ |
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 0x0 |
20 | #define USB2_PHY_OFFSET 0x38000 |
21 | /* USB3 PHY register definition */ |
22 | #define PHY_PMA_CMN_CTRL1 0xC800 |
23 | #define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0 |
24 | #define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084 |
25 | #define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR 0x0085 |
26 | #define TB_ADDR_CMN_PLL0_INTDIV 0x0094 |
27 | #define TB_ADDR_CMN_PLL0_FRACDIV 0x0095 |
28 | #define TB_ADDR_CMN_PLL0_HIGH_THR 0x0096 |
29 | #define TB_ADDR_CMN_PLL0_SS_CTRL1 0x0098 |
30 | #define TB_ADDR_CMN_PLL0_SS_CTRL2 0x0099 |
31 | #define TB_ADDR_CMN_PLL0_DSM_DIAG 0x0097 |
32 | #define TB_ADDR_CMN_DIAG_PLL0_OVRD 0x01c2 |
33 | #define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD 0x01c0 |
34 | #define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD 0x01c1 |
35 | #define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE 0x01C5 |
36 | #define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE 0x01C6 |
37 | #define TB_ADDR_CMN_DIAG_PLL0_LF_PROG 0x01C7 |
38 | #define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE 0x01c4 |
39 | #define TB_ADDR_CMN_PSM_CLK_CTRL 0x0061 |
40 | #define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR 0x40ea |
41 | #define TB_ADDR_XCVR_PSM_RCTRL 0x4001 |
42 | #define TB_ADDR_TX_PSC_A0 0x4100 |
43 | #define TB_ADDR_TX_PSC_A1 0x4101 |
44 | #define TB_ADDR_TX_PSC_A2 0x4102 |
45 | #define TB_ADDR_TX_PSC_A3 0x4103 |
46 | #define TB_ADDR_TX_DIAG_ECTRL_OVRD 0x41f5 |
47 | #define TB_ADDR_TX_PSC_CAL 0x4106 |
48 | #define TB_ADDR_TX_PSC_RDY 0x4107 |
49 | #define TB_ADDR_RX_PSC_A0 0x8000 |
50 | #define TB_ADDR_RX_PSC_A1 0x8001 |
51 | #define TB_ADDR_RX_PSC_A2 0x8002 |
52 | #define TB_ADDR_RX_PSC_A3 0x8003 |
53 | #define TB_ADDR_RX_PSC_CAL 0x8006 |
54 | #define TB_ADDR_RX_PSC_RDY 0x8007 |
55 | #define TB_ADDR_TX_TXCC_MGNLS_MULT_000 0x4058 |
56 | #define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 |
57 | #define TB_ADDR_RX_SLC_CU_ITER_TMR 0x80e3 |
58 | #define TB_ADDR_RX_SIGDET_HL_FILT_TMR 0x8090 |
59 | #define TB_ADDR_RX_SAMP_DAC_CTRL 0x8058 |
60 | #define TB_ADDR_RX_DIAG_SIGDET_TUNE 0x81dc |
61 | #define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 0x81df |
62 | #define TB_ADDR_RX_DIAG_BS_TM 0x81f5 |
63 | #define TB_ADDR_RX_DIAG_DFE_CTRL1 0x81d3 |
64 | #define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 0x81c7 |
65 | #define TB_ADDR_RX_DIAG_ILL_E_TRIM0 0x81c2 |
66 | #define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 0x81c1 |
67 | #define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 0x81c9 |
68 | #define TB_ADDR_RX_DIAG_RXFE_TM3 0x81f8 |
69 | #define TB_ADDR_RX_DIAG_RXFE_TM4 0x81f9 |
70 | #define TB_ADDR_RX_DIAG_LFPSDET_TUNE 0x81dd |
71 | #define TB_ADDR_RX_DIAG_DFE_CTRL3 0x81d5 |
72 | #define TB_ADDR_RX_DIAG_SC2C_DELAY 0x81e1 |
73 | #define TB_ADDR_RX_REE_VGA_GAIN_NODFE 0x81bf |
74 | #define TB_ADDR_XCVR_PSM_CAL_TMR 0x4002 |
75 | #define TB_ADDR_XCVR_PSM_A0BYP_TMR 0x4004 |
76 | #define TB_ADDR_XCVR_PSM_A0IN_TMR 0x4003 |
77 | #define TB_ADDR_XCVR_PSM_A1IN_TMR 0x4005 |
78 | #define TB_ADDR_XCVR_PSM_A2IN_TMR 0x4006 |
79 | #define TB_ADDR_XCVR_PSM_A3IN_TMR 0x4007 |
80 | #define TB_ADDR_XCVR_PSM_A4IN_TMR 0x4008 |
81 | #define TB_ADDR_XCVR_PSM_A5IN_TMR 0x4009 |
82 | #define TB_ADDR_XCVR_PSM_A0OUT_TMR 0x400a |
83 | #define TB_ADDR_XCVR_PSM_A1OUT_TMR 0x400b |
84 | #define TB_ADDR_XCVR_PSM_A2OUT_TMR 0x400c |
85 | #define TB_ADDR_XCVR_PSM_A3OUT_TMR 0x400d |
86 | #define TB_ADDR_XCVR_PSM_A4OUT_TMR 0x400e |
87 | #define TB_ADDR_XCVR_PSM_A5OUT_TMR 0x400f |
88 | #define TB_ADDR_TX_RCVDET_EN_TMR 0x4122 |
89 | #define TB_ADDR_TX_RCVDET_ST_TMR 0x4123 |
90 | #define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2 |
91 | #define TB_ADDR_TX_RCVDETSC_CTRL 0x4124 |
92 | |
93 | /* USB2 PHY register definition */ |
94 | #define UTMI_REG15 0xaf |
95 | #define UTMI_AFE_RX_REG0 0x0d |
96 | #define UTMI_AFE_RX_REG5 0x12 |
97 | #define UTMI_AFE_BC_REG4 0x29 |
98 | |
99 | /* Align UTMI_AFE_RX_REG0 bit[7:6] define */ |
100 | enum 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_REG15 |
112 | * |
113 | * Gate how many us for the txvalid signal until analog |
114 | * HS/FS transmitters have powered up |
115 | */ |
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 | |
123 | struct cdns_reg_pairs { |
124 | u16 val; |
125 | u32 off; |
126 | }; |
127 | |
128 | struct cdns_salvo_data { |
129 | u8 reg_offset_shift; |
130 | const struct cdns_reg_pairs *init_sequence_val; |
131 | u8 init_sequence_length; |
132 | }; |
133 | |
134 | struct 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 | |
142 | static const struct of_device_id cdns_salvo_phy_of_match[]; |
143 | static const struct cdns_salvo_data cdns_nxp_salvo_data; |
144 | |
145 | static bool cdns_is_nxp_phy(struct cdns_salvo_phy *salvo_phy) |
146 | { |
147 | return salvo_phy->data == &cdns_nxp_salvo_data; |
148 | } |
149 | |
150 | static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 offset, u32 reg) |
151 | { |
152 | return (u16)readl(addr: salvo_phy->base + offset + |
153 | reg * (1 << salvo_phy->data->reg_offset_shift)); |
154 | } |
155 | |
156 | static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, u32 offset, |
157 | u32 reg, u16 val) |
158 | { |
159 | writel(val, addr: 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 Guide |
165 | * and NXP platform tuning results. |
166 | */ |
167 | static 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 | |
246 | static 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(clk: 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: reg_pair->off, val: 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 | val: value | TXVALID_GATE_THRESHOLD_HS_0US); |
273 | |
274 | cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG5, val: 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, val: value); |
280 | |
281 | udelay(10); |
282 | |
283 | clk_disable_unprepare(clk: salvo_phy->clk); |
284 | |
285 | return ret; |
286 | } |
287 | |
288 | static 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(clk: salvo_phy->clk); |
293 | } |
294 | |
295 | static 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(clk: salvo_phy->clk); |
300 | |
301 | return 0; |
302 | } |
303 | |
304 | static 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 | else |
315 | cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4, |
316 | CLR_B_SESSION_VALID); |
317 | |
318 | return 0; |
319 | } |
320 | |
321 | static 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 | |
329 | static 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, size: 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, id: "salvo_phy_clk" ); |
344 | if (IS_ERR(ptr: salvo_phy->clk)) |
345 | return PTR_ERR(ptr: salvo_phy->clk); |
346 | |
347 | if (of_property_read_u32(np: dev->of_node, propname: "cdns,usb2-disconnect-threshold-microvolt" , out_value: &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 | else |
355 | salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_645; |
356 | |
357 | salvo_phy->base = devm_platform_ioremap_resource(pdev, index: 0); |
358 | if (IS_ERR(ptr: salvo_phy->base)) |
359 | return PTR_ERR(ptr: salvo_phy->base); |
360 | |
361 | salvo_phy->phy = devm_phy_create(dev, NULL, ops: &cdns_salvo_phy_ops); |
362 | if (IS_ERR(ptr: salvo_phy->phy)) |
363 | return PTR_ERR(ptr: salvo_phy->phy); |
364 | |
365 | phy_set_drvdata(phy: salvo_phy->phy, data: salvo_phy); |
366 | |
367 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
368 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
369 | } |
370 | |
371 | static 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 | |
377 | static 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 | }; |
384 | MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match); |
385 | |
386 | static 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 | }; |
393 | module_platform_driver(cdns_salvo_phy_driver); |
394 | |
395 | MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>" ); |
396 | MODULE_LICENSE("GPL v2" ); |
397 | MODULE_DESCRIPTION("Cadence SALVO PHY Driver" ); |
398 | |