1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013--2024 Intel Corporation
4  */
5 
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/delay.h>
9 #include <linux/device.h>
10 #include <linux/iopoll.h>
11 #include <linux/math64.h>
12 
13 #include "ipu6-bus.h"
14 #include "ipu6-isys.h"
15 #include "ipu6-platform-isys-csi2-reg.h"
16 
17 #define IPU6_DWC_DPHY_BASE(i)			(0x238038 + 0x34 * (i))
18 #define IPU6_DWC_DPHY_RSTZ			0x00
19 #define IPU6_DWC_DPHY_SHUTDOWNZ			0x04
20 #define IPU6_DWC_DPHY_HSFREQRANGE		0x08
21 #define IPU6_DWC_DPHY_CFGCLKFREQRANGE		0x0c
22 #define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE	0x10
23 #define IPU6_DWC_DPHY_TEST_IFC_REQ		0x14
24 #define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION	0x18
25 #define IPU6_DWC_DPHY_DFT_CTRL0			0x28
26 #define IPU6_DWC_DPHY_DFT_CTRL1			0x2c
27 #define IPU6_DWC_DPHY_DFT_CTRL2			0x30
28 
29 /*
30  * test IFC request definition:
31  * - req: 0 for read, 1 for write
32  * - 12 bits address
33  * - 8bits data (will ignore for read)
34  * --24----16------4-----0
35  * --|-data-|-addr-|-req-|
36  */
37 #define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
38 				  FIELD_PREP(GENMASK(15, 4), addr) | \
39 				  FIELD_PREP(GENMASK(1, 0), req))
40 
41 #define TEST_IFC_REQ_READ	0
42 #define TEST_IFC_REQ_WRITE	1
43 #define TEST_IFC_REQ_RESET	2
44 
45 #define TEST_IFC_ACCESS_MODE_FSM	0
46 #define TEST_IFC_ACCESS_MODE_IFC_CTL	1
47 
48 enum phy_fsm_state {
49 	PHY_FSM_STATE_POWERON = 0,
50 	PHY_FSM_STATE_BGPON = 1,
51 	PHY_FSM_STATE_CAL_TYPE = 2,
52 	PHY_FSM_STATE_BURNIN_CAL = 3,
53 	PHY_FSM_STATE_TERMCAL = 4,
54 	PHY_FSM_STATE_OFFSETCAL = 5,
55 	PHY_FSM_STATE_OFFSET_LANE = 6,
56 	PHY_FSM_STATE_IDLE = 7,
57 	PHY_FSM_STATE_ULP = 8,
58 	PHY_FSM_STATE_DDLTUNNING = 9,
59 	PHY_FSM_STATE_SKEW_BACKWARD = 10,
60 	PHY_FSM_STATE_INVALID,
61 };
62 
dwc_dphy_write(struct ipu6_isys * isys,u32 phy_id,u32 addr,u32 data)63 static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
64 			   u32 data)
65 {
66 	struct device *dev = &isys->adev->auxdev.dev;
67 	void __iomem *isys_base = isys->pdata->base;
68 	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
69 
70 	dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,
71 		data);
72 	writel(data, base + addr);
73 }
74 
dwc_dphy_read(struct ipu6_isys * isys,u32 phy_id,u32 addr)75 static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
76 {
77 	struct device *dev = &isys->adev->auxdev.dev;
78 	void __iomem *isys_base = isys->pdata->base;
79 	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
80 	u32 data;
81 
82 	data = readl(base + addr);
83 	dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,
84 		data);
85 
86 	return data;
87 }
88 
dwc_dphy_write_mask(struct ipu6_isys * isys,u32 phy_id,u32 addr,u32 data,u8 shift,u8 width)89 static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
90 				u32 data, u8 shift, u8 width)
91 {
92 	u32 temp;
93 	u32 mask;
94 
95 	mask = (1 << width) - 1;
96 	temp = dwc_dphy_read(isys, phy_id, addr);
97 	temp &= ~(mask << shift);
98 	temp |= (data & mask) << shift;
99 	dwc_dphy_write(isys, phy_id, addr, temp);
100 }
101 
dwc_dphy_read_mask(struct ipu6_isys * isys,u32 phy_id,u32 addr,u8 shift,u8 width)102 static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
103 					     u32 addr, u8 shift,  u8 width)
104 {
105 	u32 val;
106 
107 	val = dwc_dphy_read(isys, phy_id, addr) >> shift;
108 	return val & ((1 << width) - 1);
109 }
110 
111 #define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
dwc_dphy_ifc_read(struct ipu6_isys * isys,u32 phy_id,u32 addr,u32 * val)112 static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
113 			     u32 *val)
114 {
115 	struct device *dev = &isys->adev->auxdev.dev;
116 	void __iomem *isys_base = isys->pdata->base;
117 	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
118 	void __iomem *reg;
119 	u32 completion;
120 	int ret;
121 
122 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
123 		       IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
124 	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
125 	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
126 				 10, DWC_DPHY_TIMEOUT);
127 	if (ret) {
128 		dev_err(dev, "DWC ifc request read timeout\n");
129 		return ret;
130 	}
131 
132 	*val = completion >> 8 & 0xff;
133 	*val = FIELD_GET(GENMASK(15, 8), completion);
134 	dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);
135 
136 	return 0;
137 }
138 
dwc_dphy_ifc_write(struct ipu6_isys * isys,u32 phy_id,u32 addr,u32 data)139 static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
140 			      u32 data)
141 {
142 	struct device *dev = &isys->adev->auxdev.dev;
143 	void __iomem *isys_base = isys->pdata->base;
144 	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
145 	void __iomem *reg;
146 	u32 completion;
147 	int ret;
148 
149 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
150 		       IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
151 	completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
152 	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
153 	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
154 				 10, DWC_DPHY_TIMEOUT);
155 	if (ret)
156 		dev_err(dev, "DWC ifc request write timeout\n");
157 
158 	return ret;
159 }
160 
dwc_dphy_ifc_write_mask(struct ipu6_isys * isys,u32 phy_id,u32 addr,u32 data,u8 shift,u8 width)161 static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
162 				    u32 addr, u32 data, u8 shift, u8 width)
163 {
164 	u32 temp, mask;
165 	int ret;
166 
167 	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
168 	if (ret)
169 		return;
170 
171 	mask = (1 << width) - 1;
172 	temp &= ~(mask << shift);
173 	temp |= (data & mask) << shift;
174 	dwc_dphy_ifc_write(isys, phy_id, addr, temp);
175 }
176 
dwc_dphy_ifc_read_mask(struct ipu6_isys * isys,u32 phy_id,u32 addr,u8 shift,u8 width)177 static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
178 				  u8 shift, u8 width)
179 {
180 	int ret;
181 	u32 val;
182 
183 	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
184 	if (ret)
185 		return 0;
186 
187 	return ((val >> shift) & ((1 << width) - 1));
188 }
189 
dwc_dphy_pwr_up(struct ipu6_isys * isys,u32 phy_id)190 static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
191 {
192 	struct device *dev = &isys->adev->auxdev.dev;
193 	u32 fsm_state;
194 	int ret;
195 
196 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
197 	usleep_range(10, 20);
198 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);
199 
200 	ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
201 				(fsm_state == PHY_FSM_STATE_IDLE ||
202 				 fsm_state == PHY_FSM_STATE_ULP),
203 				100, DWC_DPHY_TIMEOUT, false, isys,
204 				phy_id, 0x1e, 0, 4);
205 
206 	if (ret)
207 		dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
208 			fsm_state);
209 
210 	return ret;
211 }
212 
213 struct dwc_dphy_freq_range {
214 	u8 hsfreq;
215 	u16 min;
216 	u16 max;
217 	u16 default_mbps;
218 	u16 osc_freq_target;
219 };
220 
221 #define DPHY_FREQ_RANGE_NUM		(63)
222 #define DPHY_FREQ_RANGE_INVALID_INDEX	(0xff)
223 static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
224 	{0x00,	80,	97,	80,	335},
225 	{0x10,	80,	107,	90,	335},
226 	{0x20,	84,	118,	100,	335},
227 	{0x30,	93,	128,	110,	335},
228 	{0x01,	103,	139,	120,	335},
229 	{0x11,	112,	149,	130,	335},
230 	{0x21,	122,	160,	140,	335},
231 	{0x31,	131,	170,	150,	335},
232 	{0x02,	141,	181,	160,	335},
233 	{0x12,	150,	191,	170,	335},
234 	{0x22,	160,	202,	180,	335},
235 	{0x32,	169,	212,	190,	335},
236 	{0x03,	183,	228,	205,	335},
237 	{0x13,	198,	244,	220,	335},
238 	{0x23,	212,	259,	235,	335},
239 	{0x33,	226,	275,	250,	335},
240 	{0x04,	250,	301,	275,	335},
241 	{0x14,	274,	328,	300,	335},
242 	{0x25,	297,	354,	325,	335},
243 	{0x35,	321,	380,	350,	335},
244 	{0x05,	369,	433,	400,	335},
245 	{0x16,	416,	485,	450,	335},
246 	{0x26,	464,	538,	500,	335},
247 	{0x37,	511,	590,	550,	335},
248 	{0x07,	559,	643,	600,	335},
249 	{0x18,	606,	695,	650,	335},
250 	{0x28,	654,	748,	700,	335},
251 	{0x39,	701,	800,	750,	335},
252 	{0x09,	749,	853,	800,	335},
253 	{0x19,	796,	905,	850,	335},
254 	{0x29,	844,	958,	900,	335},
255 	{0x3a,	891,	1010,	950,	335},
256 	{0x0a,	939,	1063,	1000,	335},
257 	{0x1a,	986,	1115,	1050,	335},
258 	{0x2a,	1034,	1168,	1100,	335},
259 	{0x3b,	1081,	1220,	1150,	335},
260 	{0x0b,	1129,	1273,	1200,	335},
261 	{0x1b,	1176,	1325,	1250,	335},
262 	{0x2b,	1224,	1378,	1300,	335},
263 	{0x3c,	1271,	1430,	1350,	335},
264 	{0x0c,	1319,	1483,	1400,	335},
265 	{0x1c,	1366,	1535,	1450,	335},
266 	{0x2c,	1414,	1588,	1500,	335},
267 	{0x3d,	1461,	1640,	1550,	208},
268 	{0x0d,	1509,	1693,	1600,	214},
269 	{0x1d,	1556,	1745,	1650,	221},
270 	{0x2e,	1604,	1798,	1700,	228},
271 	{0x3e,	1651,	1850,	1750,	234},
272 	{0x0e,	1699,	1903,	1800,	241},
273 	{0x1e,	1746,	1955,	1850,	248},
274 	{0x2f,	1794,	2008,	1900,	255},
275 	{0x3f,	1841,	2060,	1950,	261},
276 	{0x0f,	1889,	2113,	2000,	268},
277 	{0x40,	1936,	2165,	2050,	275},
278 	{0x41,	1984,	2218,	2100,	281},
279 	{0x42,	2031,	2270,	2150,	288},
280 	{0x43,	2079,	2323,	2200,	294},
281 	{0x44,	2126,	2375,	2250,	302},
282 	{0x45,	2174,	2428,	2300,	308},
283 	{0x46,	2221,	2480,	2350,	315},
284 	{0x47,	2269,	2500,	2400,	321},
285 	{0x48,	2316,	2500,	2450,	328},
286 	{0x49,	2364,	2500,	2500,	335}
287 };
288 
get_hsfreq_by_mbps(u32 mbps)289 static u16 get_hsfreq_by_mbps(u32 mbps)
290 {
291 	unsigned int i = DPHY_FREQ_RANGE_NUM;
292 
293 	while (i--) {
294 		if (freqranges[i].default_mbps == mbps ||
295 		    (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
296 			return i;
297 	}
298 
299 	return DPHY_FREQ_RANGE_INVALID_INDEX;
300 }
301 
ipu6_isys_dwc_phy_config(struct ipu6_isys * isys,u32 phy_id,u32 mbps)302 static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
303 				    u32 phy_id, u32 mbps)
304 {
305 	struct ipu6_bus_device *adev = isys->adev;
306 	struct device *dev = &adev->auxdev.dev;
307 	struct ipu6_device *isp = adev->isp;
308 	u32 cfg_clk_freqrange;
309 	u32 osc_freq_target;
310 	u32 index;
311 
312 	dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);
313 
314 	index = get_hsfreq_by_mbps(mbps);
315 	if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
316 		dev_err(dev, "link freq not found for mbps %u", mbps);
317 		return -EINVAL;
318 	}
319 
320 	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
321 			    freqranges[index].hsfreq, 0, 7);
322 
323 	/* Force termination Calibration */
324 	if (isys->phy_termcal_val) {
325 		dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
326 		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
327 		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
328 					isys->phy_termcal_val, 4, 4);
329 	}
330 
331 	/*
332 	 * Enable override to configure the DDL target oscillation
333 	 * frequency on bit 0 of register 0xe4
334 	 */
335 	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
336 	/*
337 	 * configure registers 0xe2, 0xe3 with the
338 	 * appropriate DDL target oscillation frequency
339 	 * 0x1cc(460)
340 	 */
341 	osc_freq_target = freqranges[index].osc_freq_target;
342 	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
343 				osc_freq_target & 0xff, 0, 8);
344 	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
345 				(osc_freq_target >> 8) & 0xf, 0, 4);
346 
347 	if (mbps < 1500) {
348 		/* deskew_polarity_rw, for < 1.5Gbps */
349 		dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
350 	}
351 
352 	/*
353 	 * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
354 	 * (38.4 - 17) * 4 = ~85 (0x55)
355 	 */
356 	cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
357 	dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
358 		isp->buttress.ref_clk, cfg_clk_freqrange);
359 	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
360 			    cfg_clk_freqrange, 0, 8);
361 
362 	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
363 	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);
364 
365 	return 0;
366 }
367 
ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys * isys,u32 master,u32 slave,u32 mbps)368 static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
369 					 u32 slave, u32 mbps)
370 {
371 	/* Config mastermacro */
372 	dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
373 	dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);
374 
375 	/* Config master PHY clk lane to drive long channel clk */
376 	dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
377 	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);
378 
379 	/* Config both PHYs data lanes to get clk from long channel */
380 	dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
381 	dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
382 	dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
383 	dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);
384 
385 	/* Config slave PHY clk lane to bypass long channel clk to DDR clk */
386 	dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
387 	dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);
388 
389 	/* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
390 	dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);
391 
392 	/* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
393 	dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
394 	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);
395 
396 	/* Turn off slave PHY LP-RX clk lane */
397 	dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
398 	dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
399 }
400 
401 #define PHY_E	4
ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys * isys,u32 phy_id)402 static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
403 {
404 	struct device *dev = &isys->adev->auxdev.dev;
405 	u32 rescal_done;
406 	int ret;
407 
408 	ret = dwc_dphy_pwr_up(isys, phy_id);
409 	if (ret != 0) {
410 		dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
411 		return ret;
412 	}
413 
414 	/* reset forcerxmode */
415 	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
416 	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);
417 
418 	dev_dbg(dev, "Dphy %u is ready!", phy_id);
419 
420 	if (phy_id != PHY_E || isys->phy_termcal_val)
421 		return 0;
422 
423 	usleep_range(100, 200);
424 	rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
425 	if (rescal_done) {
426 		isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
427 							       0x220, 2, 4);
428 		dev_dbg(dev, "termcal done with value = %u",
429 			isys->phy_termcal_val);
430 	}
431 
432 	return 0;
433 }
434 
ipu6_isys_dwc_phy_reset(struct ipu6_isys * isys,u32 phy_id)435 static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
436 {
437 	dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);
438 
439 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
440 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
441 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
442 		       TEST_IFC_ACCESS_MODE_FSM);
443 	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
444 		       TEST_IFC_REQ_RESET);
445 }
446 
ipu6_isys_dwc_phy_set_power(struct ipu6_isys * isys,struct ipu6_isys_csi2_config * cfg,const struct ipu6_isys_csi2_timing * timing,bool on)447 int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
448 				struct ipu6_isys_csi2_config *cfg,
449 				const struct ipu6_isys_csi2_timing *timing,
450 				bool on)
451 {
452 	struct device *dev = &isys->adev->auxdev.dev;
453 	void __iomem *isys_base = isys->pdata->base;
454 	u32 phy_id, primary, secondary;
455 	u32 nlanes, port, mbps;
456 	s64 link_freq;
457 	int ret;
458 
459 	port = cfg->port;
460 
461 	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
462 		dev_warn(dev, "invalid port ID %d\n", port);
463 		return -EINVAL;
464 	}
465 
466 	nlanes = cfg->nlanes;
467 	/* only port 0, 2 and 4 support 4 lanes */
468 	if (nlanes == 4 && port % 2) {
469 		dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
470 			nlanes);
471 		return -EINVAL;
472 	}
473 
474 	link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
475 	if (link_freq < 0) {
476 		dev_err(dev, "get link freq failed(%lld).\n", link_freq);
477 		return link_freq;
478 	}
479 
480 	mbps = div_u64(link_freq, 500000);
481 
482 	phy_id = port;
483 	primary = port & ~1;
484 	secondary = primary + 1;
485 	if (on) {
486 		if (nlanes == 4) {
487 			dev_dbg(dev, "config phy %u and %u in aggr mode\n",
488 				primary, secondary);
489 
490 			ipu6_isys_dwc_phy_reset(isys, primary);
491 			ipu6_isys_dwc_phy_reset(isys, secondary);
492 			ipu6_isys_dwc_phy_aggr_setup(isys, primary,
493 						     secondary, mbps);
494 
495 			ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
496 			if (ret)
497 				return ret;
498 			ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
499 			if (ret)
500 				return ret;
501 
502 			ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
503 			if (ret)
504 				return ret;
505 
506 			ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
507 			return ret;
508 		}
509 
510 		dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
511 			phy_id, nlanes);
512 
513 		ipu6_isys_dwc_phy_reset(isys, phy_id);
514 		ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
515 		if (ret)
516 			return ret;
517 
518 		ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
519 		return ret;
520 	}
521 
522 	if (nlanes == 4) {
523 		dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
524 			primary, secondary, port);
525 		ipu6_isys_dwc_phy_reset(isys, secondary);
526 		ipu6_isys_dwc_phy_reset(isys, primary);
527 
528 		return 0;
529 	}
530 
531 	dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);
532 
533 	ipu6_isys_dwc_phy_reset(isys, phy_id);
534 
535 	return 0;
536 }
537