537 lines
15 KiB
C
537 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2013--2024 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/math64.h>
|
|
|
|
#include "ipu6-bus.h"
|
|
#include "ipu6-isys.h"
|
|
#include "ipu6-platform-isys-csi2-reg.h"
|
|
|
|
#define IPU6_DWC_DPHY_BASE(i) (0x238038 + 0x34 * (i))
|
|
#define IPU6_DWC_DPHY_RSTZ 0x00
|
|
#define IPU6_DWC_DPHY_SHUTDOWNZ 0x04
|
|
#define IPU6_DWC_DPHY_HSFREQRANGE 0x08
|
|
#define IPU6_DWC_DPHY_CFGCLKFREQRANGE 0x0c
|
|
#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x10
|
|
#define IPU6_DWC_DPHY_TEST_IFC_REQ 0x14
|
|
#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x18
|
|
#define IPU6_DWC_DPHY_DFT_CTRL0 0x28
|
|
#define IPU6_DWC_DPHY_DFT_CTRL1 0x2c
|
|
#define IPU6_DWC_DPHY_DFT_CTRL2 0x30
|
|
|
|
/*
|
|
* test IFC request definition:
|
|
* - req: 0 for read, 1 for write
|
|
* - 12 bits address
|
|
* - 8bits data (will ignore for read)
|
|
* --24----16------4-----0
|
|
* --|-data-|-addr-|-req-|
|
|
*/
|
|
#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
|
|
FIELD_PREP(GENMASK(15, 4), addr) | \
|
|
FIELD_PREP(GENMASK(1, 0), req))
|
|
|
|
#define TEST_IFC_REQ_READ 0
|
|
#define TEST_IFC_REQ_WRITE 1
|
|
#define TEST_IFC_REQ_RESET 2
|
|
|
|
#define TEST_IFC_ACCESS_MODE_FSM 0
|
|
#define TEST_IFC_ACCESS_MODE_IFC_CTL 1
|
|
|
|
enum phy_fsm_state {
|
|
PHY_FSM_STATE_POWERON = 0,
|
|
PHY_FSM_STATE_BGPON = 1,
|
|
PHY_FSM_STATE_CAL_TYPE = 2,
|
|
PHY_FSM_STATE_BURNIN_CAL = 3,
|
|
PHY_FSM_STATE_TERMCAL = 4,
|
|
PHY_FSM_STATE_OFFSETCAL = 5,
|
|
PHY_FSM_STATE_OFFSET_LANE = 6,
|
|
PHY_FSM_STATE_IDLE = 7,
|
|
PHY_FSM_STATE_ULP = 8,
|
|
PHY_FSM_STATE_DDLTUNNING = 9,
|
|
PHY_FSM_STATE_SKEW_BACKWARD = 10,
|
|
PHY_FSM_STATE_INVALID,
|
|
};
|
|
|
|
static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
|
|
u32 data)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
void __iomem *isys_base = isys->pdata->base;
|
|
void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
|
|
|
|
dev_dbg(dev, "write: reg 0x%zx = data 0x%x", base + addr - isys_base,
|
|
data);
|
|
writel(data, base + addr);
|
|
}
|
|
|
|
static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
void __iomem *isys_base = isys->pdata->base;
|
|
void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
|
|
u32 data;
|
|
|
|
data = readl(base + addr);
|
|
dev_dbg(dev, "read: reg 0x%zx = data 0x%x", base + addr - isys_base,
|
|
data);
|
|
|
|
return data;
|
|
}
|
|
|
|
static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
|
|
u32 data, u8 shift, u8 width)
|
|
{
|
|
u32 temp;
|
|
u32 mask;
|
|
|
|
mask = (1 << width) - 1;
|
|
temp = dwc_dphy_read(isys, phy_id, addr);
|
|
temp &= ~(mask << shift);
|
|
temp |= (data & mask) << shift;
|
|
dwc_dphy_write(isys, phy_id, addr, temp);
|
|
}
|
|
|
|
static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
|
|
u32 addr, u8 shift, u8 width)
|
|
{
|
|
u32 val;
|
|
|
|
val = dwc_dphy_read(isys, phy_id, addr) >> shift;
|
|
return val & ((1 << width) - 1);
|
|
}
|
|
|
|
#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
|
|
static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
|
|
u32 *val)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
void __iomem *isys_base = isys->pdata->base;
|
|
void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
|
|
void __iomem *reg;
|
|
u32 completion;
|
|
int ret;
|
|
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
|
|
IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
|
|
reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
|
|
ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
|
|
10, DWC_DPHY_TIMEOUT);
|
|
if (ret) {
|
|
dev_err(dev, "DWC ifc request read timeout\n");
|
|
return ret;
|
|
}
|
|
|
|
*val = completion >> 8 & 0xff;
|
|
*val = FIELD_GET(GENMASK(15, 8), completion);
|
|
dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
|
|
u32 data)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
void __iomem *isys_base = isys->pdata->base;
|
|
void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
|
|
void __iomem *reg;
|
|
u32 completion;
|
|
int ret;
|
|
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
|
|
IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
|
|
completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
|
|
reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
|
|
ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
|
|
10, DWC_DPHY_TIMEOUT);
|
|
if (ret)
|
|
dev_err(dev, "DWC ifc request write timeout\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
|
|
u32 addr, u32 data, u8 shift, u8 width)
|
|
{
|
|
u32 temp, mask;
|
|
int ret;
|
|
|
|
ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
|
|
if (ret)
|
|
return;
|
|
|
|
mask = (1 << width) - 1;
|
|
temp &= ~(mask << shift);
|
|
temp |= (data & mask) << shift;
|
|
dwc_dphy_ifc_write(isys, phy_id, addr, temp);
|
|
}
|
|
|
|
static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
|
|
u8 shift, u8 width)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
|
|
if (ret)
|
|
return 0;
|
|
|
|
return ((val >> shift) & ((1 << width) - 1));
|
|
}
|
|
|
|
static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
u32 fsm_state;
|
|
int ret;
|
|
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
|
|
usleep_range(10, 20);
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);
|
|
|
|
ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
|
|
(fsm_state == PHY_FSM_STATE_IDLE ||
|
|
fsm_state == PHY_FSM_STATE_ULP),
|
|
100, DWC_DPHY_TIMEOUT, false, isys,
|
|
phy_id, 0x1e, 0, 4);
|
|
|
|
if (ret)
|
|
dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
|
|
fsm_state);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct dwc_dphy_freq_range {
|
|
u8 hsfreq;
|
|
u16 min;
|
|
u16 max;
|
|
u16 default_mbps;
|
|
u16 osc_freq_target;
|
|
};
|
|
|
|
#define DPHY_FREQ_RANGE_NUM (63)
|
|
#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff)
|
|
static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
|
|
{0x00, 80, 97, 80, 335},
|
|
{0x10, 80, 107, 90, 335},
|
|
{0x20, 84, 118, 100, 335},
|
|
{0x30, 93, 128, 110, 335},
|
|
{0x01, 103, 139, 120, 335},
|
|
{0x11, 112, 149, 130, 335},
|
|
{0x21, 122, 160, 140, 335},
|
|
{0x31, 131, 170, 150, 335},
|
|
{0x02, 141, 181, 160, 335},
|
|
{0x12, 150, 191, 170, 335},
|
|
{0x22, 160, 202, 180, 335},
|
|
{0x32, 169, 212, 190, 335},
|
|
{0x03, 183, 228, 205, 335},
|
|
{0x13, 198, 244, 220, 335},
|
|
{0x23, 212, 259, 235, 335},
|
|
{0x33, 226, 275, 250, 335},
|
|
{0x04, 250, 301, 275, 335},
|
|
{0x14, 274, 328, 300, 335},
|
|
{0x25, 297, 354, 325, 335},
|
|
{0x35, 321, 380, 350, 335},
|
|
{0x05, 369, 433, 400, 335},
|
|
{0x16, 416, 485, 450, 335},
|
|
{0x26, 464, 538, 500, 335},
|
|
{0x37, 511, 590, 550, 335},
|
|
{0x07, 559, 643, 600, 335},
|
|
{0x18, 606, 695, 650, 335},
|
|
{0x28, 654, 748, 700, 335},
|
|
{0x39, 701, 800, 750, 335},
|
|
{0x09, 749, 853, 800, 335},
|
|
{0x19, 796, 905, 850, 335},
|
|
{0x29, 844, 958, 900, 335},
|
|
{0x3a, 891, 1010, 950, 335},
|
|
{0x0a, 939, 1063, 1000, 335},
|
|
{0x1a, 986, 1115, 1050, 335},
|
|
{0x2a, 1034, 1168, 1100, 335},
|
|
{0x3b, 1081, 1220, 1150, 335},
|
|
{0x0b, 1129, 1273, 1200, 335},
|
|
{0x1b, 1176, 1325, 1250, 335},
|
|
{0x2b, 1224, 1378, 1300, 335},
|
|
{0x3c, 1271, 1430, 1350, 335},
|
|
{0x0c, 1319, 1483, 1400, 335},
|
|
{0x1c, 1366, 1535, 1450, 335},
|
|
{0x2c, 1414, 1588, 1500, 335},
|
|
{0x3d, 1461, 1640, 1550, 208},
|
|
{0x0d, 1509, 1693, 1600, 214},
|
|
{0x1d, 1556, 1745, 1650, 221},
|
|
{0x2e, 1604, 1798, 1700, 228},
|
|
{0x3e, 1651, 1850, 1750, 234},
|
|
{0x0e, 1699, 1903, 1800, 241},
|
|
{0x1e, 1746, 1955, 1850, 248},
|
|
{0x2f, 1794, 2008, 1900, 255},
|
|
{0x3f, 1841, 2060, 1950, 261},
|
|
{0x0f, 1889, 2113, 2000, 268},
|
|
{0x40, 1936, 2165, 2050, 275},
|
|
{0x41, 1984, 2218, 2100, 281},
|
|
{0x42, 2031, 2270, 2150, 288},
|
|
{0x43, 2079, 2323, 2200, 294},
|
|
{0x44, 2126, 2375, 2250, 302},
|
|
{0x45, 2174, 2428, 2300, 308},
|
|
{0x46, 2221, 2480, 2350, 315},
|
|
{0x47, 2269, 2500, 2400, 321},
|
|
{0x48, 2316, 2500, 2450, 328},
|
|
{0x49, 2364, 2500, 2500, 335}
|
|
};
|
|
|
|
static u16 get_hsfreq_by_mbps(u32 mbps)
|
|
{
|
|
unsigned int i = DPHY_FREQ_RANGE_NUM;
|
|
|
|
while (i--) {
|
|
if (freqranges[i].default_mbps == mbps ||
|
|
(mbps >= freqranges[i].min && mbps <= freqranges[i].max))
|
|
return i;
|
|
}
|
|
|
|
return DPHY_FREQ_RANGE_INVALID_INDEX;
|
|
}
|
|
|
|
static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
|
|
u32 phy_id, u32 mbps)
|
|
{
|
|
struct ipu6_bus_device *adev = isys->adev;
|
|
struct device *dev = &adev->auxdev.dev;
|
|
struct ipu6_device *isp = adev->isp;
|
|
u32 cfg_clk_freqrange;
|
|
u32 osc_freq_target;
|
|
u32 index;
|
|
|
|
dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);
|
|
|
|
index = get_hsfreq_by_mbps(mbps);
|
|
if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
|
|
dev_err(dev, "link freq not found for mbps %u", mbps);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
|
|
freqranges[index].hsfreq, 0, 7);
|
|
|
|
/* Force termination Calibration */
|
|
if (isys->phy_termcal_val) {
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
|
|
isys->phy_termcal_val, 4, 4);
|
|
}
|
|
|
|
/*
|
|
* Enable override to configure the DDL target oscillation
|
|
* frequency on bit 0 of register 0xe4
|
|
*/
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
|
|
/*
|
|
* configure registers 0xe2, 0xe3 with the
|
|
* appropriate DDL target oscillation frequency
|
|
* 0x1cc(460)
|
|
*/
|
|
osc_freq_target = freqranges[index].osc_freq_target;
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
|
|
osc_freq_target & 0xff, 0, 8);
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
|
|
(osc_freq_target >> 8) & 0xf, 0, 4);
|
|
|
|
if (mbps < 1500) {
|
|
/* deskew_polarity_rw, for < 1.5Gbps */
|
|
dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
|
|
}
|
|
|
|
/*
|
|
* Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
|
|
* (38.4 - 17) * 4 = ~85 (0x55)
|
|
*/
|
|
cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
|
|
dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
|
|
isp->buttress.ref_clk, cfg_clk_freqrange);
|
|
dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
|
|
cfg_clk_freqrange, 0, 8);
|
|
|
|
dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
|
|
dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
|
|
u32 slave, u32 mbps)
|
|
{
|
|
/* Config mastermacro */
|
|
dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);
|
|
|
|
/* Config master PHY clk lane to drive long channel clk */
|
|
dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);
|
|
|
|
/* Config both PHYs data lanes to get clk from long channel */
|
|
dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
|
|
dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);
|
|
|
|
/* Config slave PHY clk lane to bypass long channel clk to DDR clk */
|
|
dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);
|
|
|
|
/* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);
|
|
|
|
/* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);
|
|
|
|
/* Turn off slave PHY LP-RX clk lane */
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
|
|
dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
|
|
}
|
|
|
|
#define PHY_E 4
|
|
static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
u32 rescal_done;
|
|
int ret;
|
|
|
|
ret = dwc_dphy_pwr_up(isys, phy_id);
|
|
if (ret != 0) {
|
|
dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* reset forcerxmode */
|
|
dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
|
|
dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);
|
|
|
|
dev_dbg(dev, "Dphy %u is ready!", phy_id);
|
|
|
|
if (phy_id != PHY_E || isys->phy_termcal_val)
|
|
return 0;
|
|
|
|
usleep_range(100, 200);
|
|
rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
|
|
if (rescal_done) {
|
|
isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
|
|
0x220, 2, 4);
|
|
dev_dbg(dev, "termcal done with value = %u",
|
|
isys->phy_termcal_val);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
|
|
{
|
|
dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);
|
|
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
|
|
TEST_IFC_ACCESS_MODE_FSM);
|
|
dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
|
|
TEST_IFC_REQ_RESET);
|
|
}
|
|
|
|
int 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)
|
|
{
|
|
struct device *dev = &isys->adev->auxdev.dev;
|
|
void __iomem *isys_base = isys->pdata->base;
|
|
u32 phy_id, primary, secondary;
|
|
u32 nlanes, port, mbps;
|
|
s64 link_freq;
|
|
int ret;
|
|
|
|
port = cfg->port;
|
|
|
|
if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
|
|
dev_warn(dev, "invalid port ID %d\n", port);
|
|
return -EINVAL;
|
|
}
|
|
|
|
nlanes = cfg->nlanes;
|
|
/* only port 0, 2 and 4 support 4 lanes */
|
|
if (nlanes == 4 && port % 2) {
|
|
dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
|
|
nlanes);
|
|
return -EINVAL;
|
|
}
|
|
|
|
link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
|
|
if (link_freq < 0) {
|
|
dev_err(dev, "get link freq failed(%lld).\n", link_freq);
|
|
return link_freq;
|
|
}
|
|
|
|
mbps = div_u64(link_freq, 500000);
|
|
|
|
phy_id = port;
|
|
primary = port & ~1;
|
|
secondary = primary + 1;
|
|
if (on) {
|
|
if (nlanes == 4) {
|
|
dev_dbg(dev, "config phy %u and %u in aggr mode\n",
|
|
primary, secondary);
|
|
|
|
ipu6_isys_dwc_phy_reset(isys, primary);
|
|
ipu6_isys_dwc_phy_reset(isys, secondary);
|
|
ipu6_isys_dwc_phy_aggr_setup(isys, primary,
|
|
secondary, mbps);
|
|
|
|
ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
|
|
if (ret)
|
|
return ret;
|
|
ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
|
|
return ret;
|
|
}
|
|
|
|
dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
|
|
phy_id, nlanes);
|
|
|
|
ipu6_isys_dwc_phy_reset(isys, phy_id);
|
|
ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
|
|
return ret;
|
|
}
|
|
|
|
if (nlanes == 4) {
|
|
dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
|
|
primary, secondary, port);
|
|
ipu6_isys_dwc_phy_reset(isys, secondary);
|
|
ipu6_isys_dwc_phy_reset(isys, primary);
|
|
|
|
return 0;
|
|
}
|
|
|
|
dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);
|
|
|
|
ipu6_isys_dwc_phy_reset(isys, phy_id);
|
|
|
|
return 0;
|
|
}
|