mirror of
https://github.com/LiBwrt-op/openwrt-6.x.git
synced 2025-12-16 08:44:50 +00:00
The parameters must be aligned based on the last opened parenthesis (+1). If this not a multiple of the tab size (8) then the rest alignment must be done using spaces. Signed-off-by: Sven Eckelmann <sven@narfation.org> Link: https://github.com/openwrt/openwrt/pull/20906 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2896 lines
90 KiB
C
2896 lines
90 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <linux/mdio.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_mdio.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/phylink.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#define RTPCS_SDS_CNT 14
|
|
#define RTPCS_PORT_CNT 57
|
|
|
|
#define RTPCS_SPEED_10 0
|
|
#define RTPCS_SPEED_100 1
|
|
#define RTPCS_SPEED_1000 2
|
|
#define RTPCS_SPEED_10000_LEGACY 3
|
|
#define RTPCS_SPEED_10000 4
|
|
#define RTPCS_SPEED_2500 5
|
|
#define RTPCS_SPEED_5000 6
|
|
|
|
#define RTPCS_838X_CPU_PORT 28
|
|
#define RTPCS_838X_MAC_LINK_DUP_STS 0xa19c
|
|
#define RTPCS_838X_MAC_LINK_SPD_STS 0xa190
|
|
#define RTPCS_838X_MAC_LINK_STS 0xa188
|
|
#define RTPCS_838X_MAC_RX_PAUSE_STS 0xa1a4
|
|
#define RTPCS_838X_MAC_TX_PAUSE_STS 0xa1a0
|
|
|
|
#define RTPCS_839X_CPU_PORT 52
|
|
#define RTPCS_839X_MAC_LINK_DUP_STS 0x03b0
|
|
#define RTPCS_839X_MAC_LINK_SPD_STS 0x03a0
|
|
#define RTPCS_839X_MAC_LINK_STS 0x0390
|
|
#define RTPCS_839X_MAC_RX_PAUSE_STS 0x03c0
|
|
#define RTPCS_839X_MAC_TX_PAUSE_STS 0x03b8
|
|
|
|
#define RTPCS_83XX_MAC_LINK_SPD_BITS 2
|
|
|
|
#define RTPCS_930X_CPU_PORT 28
|
|
#define RTPCS_930X_MAC_LINK_DUP_STS 0xcb28
|
|
#define RTPCS_930X_MAC_LINK_SPD_STS 0xcb18
|
|
#define RTPCS_930X_MAC_LINK_STS 0xcb10
|
|
#define RTPCS_930X_MAC_RX_PAUSE_STS 0xcb30
|
|
#define RTPCS_930X_MAC_TX_PAUSE_STS 0xcb2c
|
|
|
|
#define RTPCS_931X_CPU_PORT 56
|
|
#define RTPCS_931X_MAC_LINK_DUP_STS 0x0ef0
|
|
#define RTPCS_931X_MAC_LINK_SPD_STS 0x0ed0
|
|
#define RTPCS_931X_MAC_LINK_STS 0x0ec0
|
|
#define RTPCS_931X_MAC_RX_PAUSE_STS 0x0f00
|
|
#define RTPCS_931X_MAC_TX_PAUSE_STS 0x0ef8
|
|
|
|
#define RTPCS_93XX_MAC_LINK_SPD_BITS 4
|
|
|
|
#define RTL93XX_MODEL_NAME_INFO (0x0004)
|
|
#define RTL93XX_CHIP_INFO (0x0008)
|
|
|
|
#define PHY_PAGE_2 2
|
|
#define PHY_PAGE_4 4
|
|
|
|
#define RTL9300_PHY_ID_MASK 0xf0ffffff
|
|
|
|
/* RTL930X SerDes supports the following modes:
|
|
* 0x02: SGMII 0x04: 1000BX_FIBER 0x05: FIBER100
|
|
* 0x06: QSGMII 0x09: RSGMII 0x0d: USXGMII
|
|
* 0x10: XSGMII 0x12: HISGMII 0x16: 2500Base_X
|
|
* 0x17: RXAUI_LITE 0x19: RXAUI_PLUS 0x1a: 10G Base-R
|
|
* 0x1b: 10GR1000BX_AUTO 0x1f: OFF
|
|
*/
|
|
#define RTL930X_SDS_MODE_SGMII 0x02
|
|
#define RTL930X_SDS_MODE_1000BASEX 0x04
|
|
#define RTL930X_SDS_MODE_USXGMII 0x0d
|
|
#define RTL930X_SDS_MODE_XGMII 0x10
|
|
#define RTL930X_SDS_MODE_2500BASEX 0x16
|
|
#define RTL930X_SDS_MODE_10GBASER 0x1a
|
|
#define RTL930X_SDS_OFF 0x1f
|
|
#define RTL930X_SDS_MASK 0x1f
|
|
|
|
/* RTL930X SerDes supports two submodes when mode is USXGMII:
|
|
* 0x00: USXGMII (aka USXGMII_SX)
|
|
* 0x02: 10G_QXGMII (aka USXGMII_QX)
|
|
*/
|
|
#define RTL930X_SDS_SUBMODE_USXGMII_SX 0x0
|
|
#define RTL930X_SDS_SUBMODE_USXGMII_QX 0x2
|
|
|
|
#define RTSDS_930X_PLL_1000 0x1
|
|
#define RTSDS_930X_PLL_10000 0x5
|
|
#define RTSDS_930X_PLL_2500 0x3
|
|
#define RTSDS_930X_PLL_LC 0x3
|
|
#define RTSDS_930X_PLL_RING 0x1
|
|
|
|
/* Registers of the internal SerDes of the 9310 */
|
|
#define RTL931X_SERDES_MODE_CTRL (0x13cc)
|
|
#define RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR (0x13F4)
|
|
#define RTL931X_MAC_SERDES_MODE_CTRL(sds) (0x136C + (((sds) << 2)))
|
|
|
|
struct rtpcs_ctrl {
|
|
struct device *dev;
|
|
struct regmap *map;
|
|
struct mii_bus *bus;
|
|
const struct rtpcs_config *cfg;
|
|
struct rtpcs_link *link[RTPCS_PORT_CNT];
|
|
bool rx_pol_inv[RTPCS_SDS_CNT];
|
|
bool tx_pol_inv[RTPCS_SDS_CNT];
|
|
struct mutex lock;
|
|
};
|
|
|
|
struct rtpcs_link {
|
|
struct rtpcs_ctrl *ctrl;
|
|
struct phylink_pcs pcs;
|
|
int sds;
|
|
int port;
|
|
};
|
|
|
|
struct rtpcs_config {
|
|
int cpu_port;
|
|
int mac_link_dup_sts;
|
|
int mac_link_spd_bits;
|
|
int mac_link_spd_sts;
|
|
int mac_link_sts;
|
|
int mac_rx_pause_sts;
|
|
int mac_tx_pause_sts;
|
|
const struct phylink_pcs_ops *pcs_ops;
|
|
int (*set_autoneg)(struct rtpcs_ctrl *ctrl, int sds, unsigned int neg_mode);
|
|
int (*setup_serdes)(struct rtpcs_ctrl *ctrl, int sds, phy_interface_t mode);
|
|
};
|
|
|
|
typedef struct {
|
|
u8 page;
|
|
u8 reg;
|
|
u16 data;
|
|
} sds_config;
|
|
|
|
static int rtpcs_sds_to_mmd(int sds_page, int sds_regnum)
|
|
{
|
|
return (sds_page << 8) + sds_regnum;
|
|
}
|
|
|
|
static int rtpcs_sds_read(struct rtpcs_ctrl *ctrl, int sds, int page, int regnum)
|
|
{
|
|
int mmd_regnum = rtpcs_sds_to_mmd(page, regnum);
|
|
|
|
return mdiobus_c45_read(ctrl->bus, sds, MDIO_MMD_VEND1, mmd_regnum);
|
|
}
|
|
|
|
static int rtpcs_sds_read_bits(struct rtpcs_ctrl *ctrl, int sds, int page,
|
|
int regnum, int bithigh, int bitlow)
|
|
{
|
|
int mask, val;
|
|
|
|
WARN_ON(bithigh < bitlow);
|
|
|
|
mask = GENMASK(bithigh, bitlow);
|
|
val = rtpcs_sds_read(ctrl, sds, page, regnum);
|
|
if (val < 0)
|
|
return val;
|
|
|
|
return (val & mask) >> bitlow;
|
|
}
|
|
|
|
static int rtpcs_sds_write(struct rtpcs_ctrl *ctrl, int sds, int page, int regnum, u16 value)
|
|
{
|
|
int mmd_regnum = rtpcs_sds_to_mmd(page, regnum);
|
|
|
|
return mdiobus_c45_write(ctrl->bus, sds, MDIO_MMD_VEND1, mmd_regnum, value);
|
|
}
|
|
|
|
static int rtpcs_sds_write_bits(struct rtpcs_ctrl *ctrl, int sds, int page,
|
|
int regnum, int bithigh, int bitlow, u16 value)
|
|
{
|
|
int mask, reg;
|
|
|
|
WARN_ON(bithigh < bitlow);
|
|
|
|
mask = GENMASK(bithigh, bitlow);
|
|
reg = rtpcs_sds_read(ctrl, sds, page, regnum);
|
|
if (reg < 0)
|
|
return reg;
|
|
|
|
reg = (reg & ~mask);
|
|
reg |= (value << bitlow) & mask;
|
|
|
|
return rtpcs_sds_write(ctrl, sds, page, regnum, reg);
|
|
}
|
|
|
|
static int rtpcs_sds_modify(struct rtpcs_ctrl *ctrl, int sds, int page, int regnum,
|
|
u16 mask, u16 set)
|
|
{
|
|
int mmd_regnum = rtpcs_sds_to_mmd(page, regnum);
|
|
|
|
return mdiobus_c45_modify(ctrl->bus, sds, MDIO_MMD_VEND1, mmd_regnum,
|
|
mask, set);
|
|
}
|
|
|
|
static int rtpcs_regmap_read_bits(struct rtpcs_ctrl *ctrl, int base, int bithigh, int bitlow)
|
|
{
|
|
int offset = base + (bitlow / 32) * 4;
|
|
int bits = bithigh + 1 - bitlow;
|
|
int shift = bitlow % 32;
|
|
int value;
|
|
|
|
regmap_read(ctrl->map, offset, &value);
|
|
value = (value >> shift) & (BIT(bits) - 1);
|
|
|
|
return value;
|
|
}
|
|
|
|
static struct rtpcs_link *rtpcs_phylink_pcs_to_link(struct phylink_pcs *pcs)
|
|
{
|
|
return container_of(pcs, struct rtpcs_link, pcs);
|
|
}
|
|
|
|
/* Variant-specific functions */
|
|
|
|
/* RTL930X */
|
|
|
|
/* The access registers for SDS_MODE_SEL and the LSB for each SDS within */
|
|
u16 rtpcs_930x_sds_regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a0, 0x02a0,
|
|
0x02A4, 0x02A4, 0x0198, 0x0198 };
|
|
u8 rtpcs_930x_sds_lsb[] = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6};
|
|
|
|
u16 rtpcs_930x_sds_submode_regs[] = { 0x1cc, 0x1cc, 0x2d8, 0x2d8, 0x2d8, 0x2d8,
|
|
0x2d8, 0x2d8};
|
|
u8 rtpcs_930x_sds_submode_lsb[] = { 0, 5, 0, 5, 10, 15, 20, 25 };
|
|
|
|
static void rtpcs_930x_sds_set(struct rtpcs_ctrl *ctrl, int sds_num, u32 mode)
|
|
{
|
|
pr_info("%s %d\n", __func__, mode);
|
|
if (sds_num < 0 || sds_num > 11) {
|
|
pr_err("Wrong SerDes number: %d\n", sds_num);
|
|
return;
|
|
}
|
|
|
|
regmap_write_bits(ctrl->map, rtpcs_930x_sds_regs[sds_num],
|
|
RTL930X_SDS_MASK << rtpcs_930x_sds_lsb[sds_num],
|
|
mode << rtpcs_930x_sds_lsb[sds_num]);
|
|
mdelay(10);
|
|
}
|
|
|
|
__always_unused
|
|
static u32 rtpcs_930x_sds_mode_get(struct rtpcs_ctrl *ctrl, int sds_num)
|
|
{
|
|
u32 v;
|
|
|
|
if (sds_num < 0 || sds_num > 11) {
|
|
pr_err("Wrong SerDes number: %d\n", sds_num);
|
|
return 0;
|
|
}
|
|
|
|
regmap_read(ctrl->map, rtpcs_930x_sds_regs[sds_num], &v);
|
|
v >>= rtpcs_930x_sds_lsb[sds_num];
|
|
|
|
return v & RTL930X_SDS_MASK;
|
|
}
|
|
|
|
__always_unused
|
|
static u32 rtpcs_930x_sds_submode_get(struct rtpcs_ctrl *ctrl, int sds_num)
|
|
{
|
|
u32 v;
|
|
|
|
if (sds_num < 2 || sds_num > 9) {
|
|
pr_err("%s: unsupported SerDes %d\n", __func__, sds_num);
|
|
return 0;
|
|
}
|
|
|
|
regmap_read(ctrl->map, rtpcs_930x_sds_submode_regs[sds_num], &v);
|
|
v >>= rtpcs_930x_sds_submode_lsb[sds_num];
|
|
|
|
return v & RTL930X_SDS_MASK;
|
|
}
|
|
|
|
static void rtpcs_930x_sds_submode_set(struct rtpcs_ctrl *ctrl, int sds,
|
|
u32 submode)
|
|
{
|
|
if (sds < 2 || sds > 9) {
|
|
pr_err("%s: submode unsupported on serdes %d\n", __func__, sds);
|
|
return;
|
|
}
|
|
|
|
if (submode != RTL930X_SDS_SUBMODE_USXGMII_SX &&
|
|
submode != RTL930X_SDS_SUBMODE_USXGMII_QX) {
|
|
pr_err("%s: unsupported submode 0x%x\n", __func__, submode);
|
|
}
|
|
|
|
regmap_write_bits(ctrl->map, rtpcs_930x_sds_submode_regs[sds - 2],
|
|
RTL930X_SDS_MASK << rtpcs_930x_sds_submode_lsb[sds - 2],
|
|
submode << rtpcs_930x_sds_submode_lsb[sds - 2]);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rx_reset(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_if)
|
|
{
|
|
int page = 0x2e; /* 10GR and USXGMII */
|
|
|
|
if (phy_if == PHY_INTERFACE_MODE_1000BASEX)
|
|
page = 0x24;
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds_num, page, 0x15, 4, 4, 0x1);
|
|
mdelay(5);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, page, 0x15, 4, 4, 0x0);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_get_pll_data(struct rtpcs_ctrl *ctrl, int sds,
|
|
int *pll, int *speed)
|
|
{
|
|
int sbit, pbit = sds & 1 ? 6 : 4;
|
|
int base_sds = sds & ~1;
|
|
|
|
/*
|
|
* PLL data is shared between adjacent SerDes in the even lane. Each SerDes defines
|
|
* what PLL it needs (ring or LC) while the PLL itself stores the current speed.
|
|
*/
|
|
|
|
*pll = rtpcs_sds_read_bits(ctrl, base_sds, 0x20, 0x12, pbit + 1, pbit);
|
|
sbit = *pll == RTSDS_930X_PLL_LC ? 8 : 12;
|
|
*speed = rtpcs_sds_read_bits(ctrl, base_sds, 0x20, 0x12, sbit + 3, sbit);
|
|
}
|
|
|
|
static int rtpcs_930x_sds_set_pll_data(struct rtpcs_ctrl *ctrl, int sds,
|
|
int pll, int speed)
|
|
{
|
|
int sbit = pll == RTSDS_930X_PLL_LC ? 8 : 12;
|
|
int pbit = sds & 1 ? 6 : 4;
|
|
int base_sds = sds & ~1;
|
|
|
|
if ((speed != RTSDS_930X_PLL_1000) &&
|
|
(speed != RTSDS_930X_PLL_2500) &&
|
|
(speed != RTSDS_930X_PLL_10000))
|
|
return -EINVAL;
|
|
|
|
if ((pll != RTSDS_930X_PLL_RING) && (pll != RTSDS_930X_PLL_LC))
|
|
return -EINVAL;
|
|
|
|
if ((pll == RTSDS_930X_PLL_RING) && (speed == RTSDS_930X_PLL_10000))
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* A SerDes clock can either be taken from the low speed ring PLL or the high speed
|
|
* LC PLL. As it is unclear if disabling PLLs has any positive or negative effect,
|
|
* always activate both.
|
|
*/
|
|
|
|
rtpcs_sds_write_bits(ctrl, base_sds, 0x20, 0x12, 3, 0, 0xf);
|
|
rtpcs_sds_write_bits(ctrl, base_sds, 0x20, 0x12, pbit + 1, pbit, pll);
|
|
rtpcs_sds_write_bits(ctrl, base_sds, 0x20, 0x12, sbit + 3, sbit, speed);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rtpcs_930x_sds_reset_cmu(struct rtpcs_ctrl *ctrl, int sds)
|
|
{
|
|
int reset_sequence[4] = { 3, 2, 3, 1 };
|
|
int base_sds = sds & ~1;
|
|
int pll, speed, i, bit;
|
|
|
|
/*
|
|
* After the PLL speed has changed, the CMU must take over the new values. The models
|
|
* of the Otto platform have different reset sequences. Luckily it always boils down
|
|
* to flipping two bits in a special sequence.
|
|
*/
|
|
|
|
rtpcs_930x_sds_get_pll_data(ctrl, sds, &pll, &speed);
|
|
bit = pll == RTSDS_930X_PLL_LC ? 2 : 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(reset_sequence); i++)
|
|
rtpcs_sds_write_bits(ctrl, base_sds, 0x21, 0x0b, bit + 1, bit,
|
|
reset_sequence[i]);
|
|
}
|
|
|
|
static int rtpcs_930x_sds_wait_clock_ready(struct rtpcs_ctrl *ctrl, int sds)
|
|
{
|
|
int i, base_sds = sds & ~1, ready, ready_cnt = 0, bit = (sds & 1) + 4;
|
|
|
|
/*
|
|
* While reconfiguring a SerDes it might take some time until its clock is in sync with
|
|
* the PLL. During that timespan the ready signal might toggle randomly. According to
|
|
* GPL sources it is enough to verify that 3 consecutive clock ready checks say "ok".
|
|
*/
|
|
|
|
for (i = 0; i < 20; i++) {
|
|
usleep_range(10000, 15000);
|
|
|
|
rtpcs_sds_write(ctrl, base_sds, 0x1f, 0x02, 53);
|
|
ready = rtpcs_sds_read_bits(ctrl, base_sds, 0x1f, 0x14, bit, bit);
|
|
|
|
ready_cnt = ready ? ready_cnt + 1 : 0;
|
|
if (ready_cnt >= 3)
|
|
return 0;
|
|
}
|
|
|
|
return -EBUSY;
|
|
}
|
|
|
|
static int rtpcs_930x_sds_get_internal_mode(struct rtpcs_ctrl *ctrl, int sds)
|
|
{
|
|
return rtpcs_sds_read_bits(ctrl, sds, 0x1f, 0x09, 11, 7);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_set_internal_mode(struct rtpcs_ctrl *ctrl, int sds,
|
|
int mode)
|
|
{
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0x09, 6, 6, 0x1); /* Force mode enable */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0x09, 11, 7, mode);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_set_power(struct rtpcs_ctrl *ctrl, int sds, bool on)
|
|
{
|
|
int power_down = on ? 0x0 : 0x3;
|
|
int rx_enable = on ? 0x3 : 0x1;
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x20, 0x00, 7, 6, power_down);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x20, 0x00, 5, 4, rx_enable);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_reconfigure_pll(struct rtpcs_ctrl *ctrl, int sds, int pll)
|
|
{
|
|
int mode, tmp, speed;
|
|
|
|
mode = rtpcs_930x_sds_get_internal_mode(ctrl, sds);
|
|
rtpcs_930x_sds_get_pll_data(ctrl, sds, &tmp, &speed);
|
|
|
|
rtpcs_930x_sds_set_power(ctrl, sds, false);
|
|
rtpcs_930x_sds_set_internal_mode(ctrl, sds, RTL930X_SDS_OFF);
|
|
|
|
rtpcs_930x_sds_set_pll_data(ctrl, sds, pll, speed);
|
|
rtpcs_930x_sds_reset_cmu(ctrl, sds);
|
|
|
|
rtpcs_930x_sds_set_internal_mode(ctrl, sds, mode);
|
|
if (rtpcs_930x_sds_wait_clock_ready(ctrl, sds))
|
|
pr_err("%s: SDS %d could not sync clock\n", __func__, sds);
|
|
|
|
rtpcs_930x_sds_set_power(ctrl, sds, true);
|
|
}
|
|
|
|
static int rtpcs_930x_sds_config_pll(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t interface)
|
|
{
|
|
int neighbor_speed, neighbor_mode, neighbor_pll, neighbor = sds ^ 1;
|
|
bool speed_changed = true;
|
|
int pll, speed;
|
|
|
|
/*
|
|
* A SerDes pair on the RTL930x is driven by two PLLs. A low speed ring PLL can generate
|
|
* signals of 1.25G and 3.125G for link speeds of 1G/2.5G. A high speed LC PLL can
|
|
* additionally generate a 10.3125G signal for 10G speeds. To drive the pair at different
|
|
* speeds each SerDes must use its own PLL. But what if the SerDess attached to the ring
|
|
* PLL suddenly needs 10G but the LC PLL is running at 1G? To avoid reconfiguring the
|
|
* "partner" SerDes we must choose wisely what assignment serves the current needs. The
|
|
* logic boils down to the following rules:
|
|
*
|
|
* - Use ring PLL for slow 1G speeds
|
|
* - Use LC PLL for fast 10G speeds
|
|
* - For 2.5G prefer ring over LC PLL
|
|
*
|
|
* However, when we want to configure 10G speed while the other SerDes is already using
|
|
* the LC PLL for a slower speed, there is no way to avoid reconfiguration. Note that
|
|
* this can even happen when the other SerDes is not actually in use, because changing
|
|
* the state of a SerDes back to RTL930X_SDS_OFF is not (yet) implemented.
|
|
*/
|
|
|
|
neighbor_mode = rtpcs_930x_sds_get_internal_mode(ctrl, neighbor);
|
|
rtpcs_930x_sds_get_pll_data(ctrl, neighbor, &neighbor_pll, &neighbor_speed);
|
|
|
|
if ((interface == PHY_INTERFACE_MODE_1000BASEX) ||
|
|
(interface == PHY_INTERFACE_MODE_SGMII))
|
|
speed = RTSDS_930X_PLL_1000;
|
|
else if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
|
speed = RTSDS_930X_PLL_2500;
|
|
else if (interface == PHY_INTERFACE_MODE_10GBASER)
|
|
speed = RTSDS_930X_PLL_10000;
|
|
else
|
|
return -ENOTSUPP;
|
|
|
|
if (!neighbor_mode)
|
|
pll = speed == RTSDS_930X_PLL_10000 ? RTSDS_930X_PLL_LC : RTSDS_930X_PLL_RING;
|
|
else if (speed == neighbor_speed) {
|
|
speed_changed = false;
|
|
pll = neighbor_pll;
|
|
} else if (neighbor_pll == RTSDS_930X_PLL_RING)
|
|
pll = RTSDS_930X_PLL_LC;
|
|
else if (speed == RTSDS_930X_PLL_10000) {
|
|
pr_info("%s: SDS %d needs LC PLL, reconfigure SDS %d to use ring PLL\n",
|
|
__func__, sds, neighbor);
|
|
rtpcs_930x_sds_reconfigure_pll(ctrl, neighbor, RTSDS_930X_PLL_RING);
|
|
pll = RTSDS_930X_PLL_LC;
|
|
} else
|
|
pll = RTSDS_930X_PLL_RING;
|
|
|
|
rtpcs_930x_sds_set_pll_data(ctrl, sds, pll, speed);
|
|
|
|
if (speed_changed)
|
|
rtpcs_930x_sds_reset_cmu(ctrl, sds);
|
|
|
|
pr_info("%s: SDS %d using %s PLL for %s\n", __func__, sds,
|
|
pll == RTSDS_930X_PLL_LC ? "LC" : "ring", phy_modes(interface));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rtpcs_930x_sds_reset_state_machine(struct rtpcs_ctrl *ctrl, int sds)
|
|
{
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x02, 12, 12, 0x01); /* SM_RESET bit */
|
|
usleep_range(10000, 20000);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x02, 12, 12, 0x00);
|
|
usleep_range(10000, 20000);
|
|
}
|
|
|
|
static int rtpcs_930x_sds_init_state_machine(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t interface)
|
|
{
|
|
int loopback, link, cnt = 20, ret = -EBUSY;
|
|
|
|
if (interface != PHY_INTERFACE_MODE_10GBASER)
|
|
return 0;
|
|
/*
|
|
* After a SerDes mode change it takes some time until the frontend state machine
|
|
* works properly for 10G. To verify operation readyness run a connection check via
|
|
* loopback.
|
|
*/
|
|
loopback = rtpcs_sds_read_bits(ctrl, sds, 0x06, 0x01, 2, 2); /* CFG_AFE_LPK bit */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x01, 2, 2, 0x01);
|
|
|
|
while (cnt-- && ret) {
|
|
rtpcs_930x_sds_reset_state_machine(ctrl, sds);
|
|
link = rtpcs_sds_read_bits(ctrl, sds, 0x05, 0x00, 12, 12); /* 10G link state (latched) */
|
|
link = rtpcs_sds_read_bits(ctrl, sds, 0x05, 0x00, 12, 12);
|
|
if (link)
|
|
ret = 0;
|
|
}
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x01, 2, 2, loopback);
|
|
rtpcs_930x_sds_reset_state_machine(ctrl, sds);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rtpcs_930x_sds_force_mode(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t interface)
|
|
{
|
|
int mode;
|
|
|
|
/*
|
|
* TODO: Usually one would expect that it is enough to modify the SDS_MODE_SEL_*
|
|
* registers (lets call it MAC setup). It seems as if this complex sequence is only
|
|
* needed for modes that cannot be set by the SoC itself. Additionally it is unclear
|
|
* if this sequence should quit early in case of errors.
|
|
*/
|
|
|
|
switch (interface) {
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
mode = RTL930X_SDS_MODE_SGMII;
|
|
break;
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
mode = RTL930X_SDS_MODE_1000BASEX;
|
|
break;
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
mode = RTL930X_SDS_MODE_2500BASEX;
|
|
break;
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
mode = RTL930X_SDS_MODE_10GBASER;
|
|
break;
|
|
case PHY_INTERFACE_MODE_NA:
|
|
mode = RTL930X_SDS_OFF;
|
|
break;
|
|
default:
|
|
pr_err("%s: SDS %d does not support %s\n", __func__, sds, phy_modes(interface));
|
|
return;
|
|
}
|
|
|
|
rtpcs_930x_sds_set_power(ctrl, sds, false);
|
|
rtpcs_930x_sds_set_internal_mode(ctrl, sds, RTL930X_SDS_OFF);
|
|
if (interface == PHY_INTERFACE_MODE_NA)
|
|
return;
|
|
|
|
if (rtpcs_930x_sds_config_pll(ctrl, sds, interface))
|
|
pr_err("%s: SDS %d could not configure PLL for %s\n", __func__,
|
|
sds, phy_modes(interface));
|
|
|
|
rtpcs_930x_sds_set_internal_mode(ctrl, sds, mode);
|
|
if (rtpcs_930x_sds_wait_clock_ready(ctrl, sds))
|
|
pr_err("%s: SDS %d could not sync clock\n", __func__, sds);
|
|
|
|
if (rtpcs_930x_sds_init_state_machine(ctrl, sds, interface))
|
|
pr_err("%s: SDS %d could not reset state machine\n", __func__, sds);
|
|
|
|
rtpcs_930x_sds_set_power(ctrl, sds, true);
|
|
rtpcs_930x_sds_rx_reset(ctrl, sds, interface);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_mode_set(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
u32 mode;
|
|
u32 submode;
|
|
|
|
if (sds < 0 || sds > 11) {
|
|
pr_err("%s: invalid SerDes number: %d\n", __func__, sds);
|
|
return;
|
|
}
|
|
|
|
switch (phy_mode) {
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
rtpcs_930x_sds_force_mode(ctrl, sds, phy_mode);
|
|
return;
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
mode = RTL930X_SDS_MODE_USXGMII;
|
|
submode = RTL930X_SDS_SUBMODE_USXGMII_QX;
|
|
break;
|
|
default:
|
|
pr_warn("%s: unsupported mode %s\n", __func__, phy_modes(phy_mode));
|
|
return;
|
|
}
|
|
|
|
/* SerDes off first. */
|
|
rtpcs_930x_sds_set(ctrl, sds, RTL930X_SDS_OFF);
|
|
|
|
/* Set the mode. */
|
|
rtpcs_930x_sds_set(ctrl, sds, mode);
|
|
|
|
/* Set the submode if needed. */
|
|
if (phy_mode == PHY_INTERFACE_MODE_10G_QXGMII)
|
|
rtpcs_930x_sds_submode_set(ctrl, sds, submode);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_tx_config(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t phy_if)
|
|
{
|
|
/* parameters: rtl9303_80G_txParam_s2 */
|
|
int impedance = 0x8;
|
|
int pre_amp = 0x2;
|
|
int main_amp = 0x9;
|
|
int post_amp = 0x2;
|
|
int pre_en = 0x1;
|
|
int post_en = 0x1;
|
|
int page;
|
|
|
|
switch (phy_if) {
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
pre_amp = 0x1;
|
|
main_amp = 0x9;
|
|
post_amp = 0x1;
|
|
page = 0x25;
|
|
break;
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
pre_amp = 0;
|
|
post_amp = 0x8;
|
|
pre_en = 0;
|
|
page = 0x29;
|
|
break;
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
case PHY_INTERFACE_MODE_USXGMII:
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
pre_en = 0;
|
|
pre_amp = 0;
|
|
main_amp = 0x10;
|
|
post_amp = 0;
|
|
post_en = 0;
|
|
page = 0x2f;
|
|
break;
|
|
default:
|
|
pr_err("%s: unsupported PHY mode\n", __func__);
|
|
return;
|
|
}
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x01, 15, 11, pre_amp);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x06, 4, 0, post_amp);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x07, 0, 0, pre_en);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x07, 3, 3, post_en);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x07, 8, 4, main_amp);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x18, 15, 12, impedance);
|
|
}
|
|
|
|
/* Wait for clock ready, this assumes the SerDes is in XGMII mode
|
|
* timeout is in ms
|
|
*/
|
|
__always_unused
|
|
static int rtpcs_930x_sds_clock_wait(struct rtpcs_ctrl *ctrl, int timeout)
|
|
{
|
|
u32 v;
|
|
unsigned long start = jiffies;
|
|
unsigned long end = start + (HZ / 1000) * timeout;
|
|
|
|
do {
|
|
rtpcs_sds_write_bits(ctrl, 2, 0x1f, 0x2, 15, 0, 53);
|
|
v = rtpcs_sds_read_bits(ctrl, 2, 0x1f, 20, 5, 4);
|
|
if (v == 3)
|
|
return 0;
|
|
} while (time_before(jiffies, end));
|
|
|
|
return 1;
|
|
}
|
|
|
|
__always_unused
|
|
static void rtpcs_930x_sds_rxcal_dcvs_manual(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
u32 dcvs_id, bool manual, u32 dvcs_list[])
|
|
{
|
|
if (manual) {
|
|
switch (dcvs_id) {
|
|
case 0:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 14, 14, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 5, 5, dvcs_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 4, 0, dvcs_list[1]);
|
|
break;
|
|
case 1:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 13, 13, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 15, 15, dvcs_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 14, 11, dvcs_list[1]);
|
|
break;
|
|
case 2:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 12, 12, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 10, 10, dvcs_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 9, 6, dvcs_list[1]);
|
|
break;
|
|
case 3:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 11, 11, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 5, 5, dvcs_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 4, 1, dvcs_list[1]);
|
|
break;
|
|
case 4:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x01, 15, 15, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 10, 10, dvcs_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 9, 6, dvcs_list[1]);
|
|
break;
|
|
case 5:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x02, 11, 11, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 4, 4, dvcs_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 3, 0, dvcs_list[1]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
switch (dcvs_id) {
|
|
case 0:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 14, 14, 0x0);
|
|
break;
|
|
case 1:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 13, 13, 0x0);
|
|
break;
|
|
case 2:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 12, 12, 0x0);
|
|
break;
|
|
case 3:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 11, 11, 0x0);
|
|
break;
|
|
case 4:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x01, 15, 15, 0x0);
|
|
break;
|
|
case 5:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x02, 11, 11, 0x0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
mdelay(1);
|
|
}
|
|
}
|
|
|
|
__always_unused
|
|
static void rtpcs_930x_sds_rxcal_dcvs_get(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
u32 dcvs_id, u32 dcvs_list[])
|
|
{
|
|
u32 dcvs_sign_out = 0, dcvs_coef_bin = 0;
|
|
bool dcvs_manual;
|
|
|
|
if (!(sds_num % 2))
|
|
rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f);
|
|
else
|
|
rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31);
|
|
|
|
/* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1);
|
|
|
|
/* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20);
|
|
|
|
switch (dcvs_id) {
|
|
case 0:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x22);
|
|
mdelay(1);
|
|
|
|
/* ##DCVS0 Read Out */
|
|
dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4);
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0);
|
|
dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 14, 14);
|
|
break;
|
|
|
|
case 1:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x23);
|
|
mdelay(1);
|
|
|
|
/* ##DCVS0 Read Out */
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4);
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0);
|
|
dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 13, 13);
|
|
break;
|
|
|
|
case 2:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x24);
|
|
mdelay(1);
|
|
|
|
/* ##DCVS0 Read Out */
|
|
dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4);
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0);
|
|
dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 12, 12);
|
|
break;
|
|
case 3:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x25);
|
|
mdelay(1);
|
|
|
|
/* ##DCVS0 Read Out */
|
|
dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4);
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0);
|
|
dcvs_manual = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 11, 11);
|
|
break;
|
|
|
|
case 4:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x2c);
|
|
mdelay(1);
|
|
|
|
/* ##DCVS0 Read Out */
|
|
dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4);
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0);
|
|
dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x01, 15, 15);
|
|
break;
|
|
|
|
case 5:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x2d);
|
|
mdelay(1);
|
|
|
|
/* ##DCVS0 Read Out */
|
|
dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4);
|
|
dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0);
|
|
dcvs_manual = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x02, 11, 11);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (dcvs_sign_out)
|
|
pr_info("%s DCVS %u Sign: -", __func__, dcvs_id);
|
|
else
|
|
pr_info("%s DCVS %u Sign: +", __func__, dcvs_id);
|
|
|
|
pr_info("DCVS %u even coefficient = %u", dcvs_id, dcvs_coef_bin);
|
|
pr_info("DCVS %u manual = %u", dcvs_id, dcvs_manual);
|
|
|
|
dcvs_list[0] = dcvs_sign_out;
|
|
dcvs_list[1] = dcvs_coef_bin;
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_leq_manual(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
bool manual, u32 leq_gray)
|
|
{
|
|
if (manual) {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x18, 15, 15, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x16, 14, 10, leq_gray);
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x18, 15, 15, 0x0);
|
|
mdelay(100);
|
|
}
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_leq_offset_manual(struct rtpcs_ctrl *ctrl,
|
|
u32 sds_num, bool manual,
|
|
u32 offset)
|
|
{
|
|
if (manual) {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 6, 2, offset);
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 6, 2, offset);
|
|
mdelay(1);
|
|
}
|
|
}
|
|
|
|
#define GRAY_BITS 5
|
|
static u32 rtpcs_930x_sds_rxcal_gray_to_binary(u32 gray_code)
|
|
{
|
|
int i, j, m;
|
|
u32 g[GRAY_BITS];
|
|
u32 c[GRAY_BITS];
|
|
u32 leq_binary = 0;
|
|
|
|
for (i = 0; i < GRAY_BITS; i++)
|
|
g[i] = (gray_code & BIT(i)) >> i;
|
|
|
|
m = GRAY_BITS - 1;
|
|
|
|
c[m] = g[m];
|
|
|
|
for (i = 0; i < m; i++) {
|
|
c[i] = g[i];
|
|
for (j = i + 1; j < GRAY_BITS; j++)
|
|
c[i] = c[i] ^ g[j];
|
|
}
|
|
|
|
for (i = 0; i < GRAY_BITS; i++)
|
|
leq_binary += c[i] << i;
|
|
|
|
return leq_binary;
|
|
}
|
|
|
|
static u32 rtpcs_930x_sds_rxcal_leq_read(struct rtpcs_ctrl *ctrl, int sds_num)
|
|
{
|
|
u32 leq_gray, leq_bin;
|
|
bool leq_manual;
|
|
|
|
if (!(sds_num % 2))
|
|
rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f);
|
|
else
|
|
rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31);
|
|
|
|
/* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1);
|
|
|
|
/* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[0 1 x x x x] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x10);
|
|
mdelay(1);
|
|
|
|
/* ##LEQ Read Out */
|
|
leq_gray = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 7, 3);
|
|
leq_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x18, 15, 15);
|
|
leq_bin = rtpcs_930x_sds_rxcal_gray_to_binary(leq_gray);
|
|
|
|
pr_info("LEQ_gray: %u, LEQ_bin: %u", leq_gray, leq_bin);
|
|
pr_info("LEQ manual: %u", leq_manual);
|
|
|
|
return leq_bin;
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_vth_manual(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
bool manual, u32 vth_list[])
|
|
{
|
|
if (manual) {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, 13, 13, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x13, 5, 3, vth_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x13, 2, 0, vth_list[1]);
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, 13, 13, 0x0);
|
|
mdelay(10);
|
|
}
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_vth_get(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
u32 vth_list[])
|
|
{
|
|
u32 vth_manual;
|
|
|
|
/* ##Page0x1F, Reg0x02[15 0], REG_DBGO_SEL=[0x002F]; */ /* Lane0 */
|
|
/* ##Page0x1F, Reg0x02[15 0], REG_DBGO_SEL=[0x0031]; */ /* Lane1 */
|
|
if (!(sds_num % 2))
|
|
rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f);
|
|
else
|
|
rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31);
|
|
|
|
/* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1);
|
|
/* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20);
|
|
/* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 0 0] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0xc);
|
|
|
|
mdelay(1);
|
|
|
|
/* ##VthP & VthN Read Out */
|
|
vth_list[0] = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 2, 0); /* v_thp set bin */
|
|
vth_list[1] = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 3); /* v_thn set bin */
|
|
|
|
pr_info("vth_set_bin = %d", vth_list[0]);
|
|
pr_info("vth_set_bin = %d", vth_list[1]);
|
|
|
|
vth_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x0f, 13, 13);
|
|
pr_info("Vth Maunal = %d", vth_manual);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_tap_manual(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
int tap_id, bool manual, u32 tap_list[])
|
|
{
|
|
if (manual) {
|
|
switch (tap_id) {
|
|
case 0:
|
|
/* ##REG0_LOAD_IN_INIT[0]=1; REG0_TAP0_INIT[5:0]=Tap0_Value */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 5, 5, tap_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 4, 0, tap_list[1]);
|
|
break;
|
|
case 1:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x07, 6, 6, tap_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x09, 11, 6, tap_list[1]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x07, 5, 5, tap_list[2]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x12, 5, 0, tap_list[3]);
|
|
break;
|
|
case 2:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x09, 5, 5, tap_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x09, 4, 0, tap_list[1]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 11, 11, tap_list[2]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 10, 6, tap_list[3]);
|
|
break;
|
|
case 3:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 5, 5, tap_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 4, 0, tap_list[1]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 5, 5, tap_list[2]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 4, 0, tap_list[3]);
|
|
break;
|
|
case 4:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x01, 5, 5, tap_list[0]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x01, 4, 0, tap_list[1]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 11, 11, tap_list[2]);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 10, 6, tap_list[3]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x0);
|
|
mdelay(10);
|
|
}
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_tap_get(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
u32 tap_id, u32 tap_list[])
|
|
{
|
|
u32 tap0_sign_out;
|
|
u32 tap0_coef_bin;
|
|
u32 tap_sign_out_even;
|
|
u32 tap_coef_bin_even;
|
|
u32 tap_sign_out_odd;
|
|
u32 tap_coef_bin_odd;
|
|
bool tap_manual;
|
|
|
|
if (!(sds_num % 2))
|
|
rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f);
|
|
else
|
|
rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31);
|
|
|
|
/* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1);
|
|
/* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20);
|
|
|
|
if (!tap_id) {
|
|
/* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 0 0 1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0);
|
|
/* ##Tap1 Even Read Out */
|
|
mdelay(1);
|
|
tap0_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 5);
|
|
tap0_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 0);
|
|
|
|
if (tap0_sign_out == 1)
|
|
pr_info("Tap0 Sign : -");
|
|
else
|
|
pr_info("Tap0 Sign : +");
|
|
|
|
pr_info("tap0_coef_bin = %d", tap0_coef_bin);
|
|
|
|
tap_list[0] = tap0_sign_out;
|
|
tap_list[1] = tap0_coef_bin;
|
|
|
|
tap_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x0f, 7, 7);
|
|
pr_info("tap0 manual = %u", tap_manual);
|
|
} else {
|
|
/* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 0 0 1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, tap_id);
|
|
mdelay(1);
|
|
/* ##Tap1 Even Read Out */
|
|
tap_sign_out_even = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 5);
|
|
tap_coef_bin_even = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 0);
|
|
|
|
/* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 1 1 0] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, (tap_id + 5));
|
|
/* ##Tap1 Odd Read Out */
|
|
tap_sign_out_odd = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 5);
|
|
tap_coef_bin_odd = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 0);
|
|
|
|
if (tap_sign_out_even == 1)
|
|
pr_info("Tap %u even sign: -", tap_id);
|
|
else
|
|
pr_info("Tap %u even sign: +", tap_id);
|
|
|
|
pr_info("Tap %u even coefficient = %u", tap_id, tap_coef_bin_even);
|
|
|
|
if (tap_sign_out_odd == 1)
|
|
pr_info("Tap %u odd sign: -", tap_id);
|
|
else
|
|
pr_info("Tap %u odd sign: +", tap_id);
|
|
|
|
pr_info("Tap %u odd coefficient = %u", tap_id, tap_coef_bin_odd);
|
|
|
|
tap_list[0] = tap_sign_out_even;
|
|
tap_list[1] = tap_coef_bin_even;
|
|
tap_list[2] = tap_sign_out_odd;
|
|
tap_list[3] = tap_coef_bin_odd;
|
|
|
|
tap_manual = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7);
|
|
pr_info("tap %u manual = %d", tap_id, tap_manual);
|
|
}
|
|
}
|
|
|
|
__always_unused
|
|
static void rtpcs_930x_sds_do_rx_calibration_1(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
/* From both rtl9300_rxCaliConf_serdes_myParam and rtl9300_rxCaliConf_phy_myParam */
|
|
int tap0_init_val = 0x1f; /* Initial Decision Fed Equalizer 0 tap */
|
|
int vth_min = 0x0;
|
|
|
|
pr_info("start_1.1.1 initial value for sds %d\n", sds);
|
|
rtpcs_sds_write(ctrl, sds, 6, 0, 0);
|
|
|
|
/* FGCAL */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x01, 14, 14, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1c, 10, 5, 0x20);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x02, 0, 0, 0x01);
|
|
|
|
/* DCVS */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1e, 14, 11, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x01, 15, 15, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x02, 11, 11, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1c, 4, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 15, 11, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 10, 6, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 5, 1, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x02, 10, 6, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x11, 4, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x00, 3, 0, 0x0f);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x04, 6, 6, 0x01);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x04, 7, 7, 0x01);
|
|
|
|
/* LEQ (Long Term Equivalent signal level) */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x16, 14, 8, 0x00);
|
|
|
|
/* DFE (Decision Fed Equalizer) */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x03, 5, 0, tap0_init_val);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x09, 11, 6, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x09, 5, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0a, 5, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x01, 5, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x12, 5, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0a, 11, 6, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x06, 5, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x01, 5, 0, 0x00);
|
|
|
|
/* Vth */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x13, 5, 3, 0x07);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x13, 2, 0, 0x07);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x0b, 5, 3, vth_min);
|
|
|
|
pr_info("end_1.1.1 --\n");
|
|
|
|
pr_info("start_1.1.2 Load DFE init. value\n");
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0f, 13, 7, 0x7f);
|
|
|
|
pr_info("end_1.1.2\n");
|
|
|
|
pr_info("start_1.1.3 disable LEQ training,enable DFE clock\n");
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x17, 7, 7, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x17, 6, 2, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0c, 8, 8, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0b, 4, 4, 0x01);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x12, 14, 14, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x02, 15, 15, 0x00);
|
|
|
|
pr_info("end_1.1.3 --\n");
|
|
|
|
pr_info("start_1.1.4 offset cali setting\n");
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0f, 15, 14, 0x03);
|
|
|
|
pr_info("end_1.1.4\n");
|
|
|
|
pr_info("start_1.1.5 LEQ and DFE setting\n");
|
|
|
|
/* TODO: make this work for DAC cables of different lengths */
|
|
/* For a 10GBit serdes wit Fibre, SDS 8 or 9 */
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER ||
|
|
phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
|
|
phy_mode == PHY_INTERFACE_MODE_SGMII)
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x16, 3, 2, 0x02);
|
|
else
|
|
pr_err("%s not PHY-based or SerDes, implement DAC!\n", __func__);
|
|
|
|
/* No serdes, check for Aquantia PHYs */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x16, 3, 2, 0x02);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0f, 6, 0, 0x5f);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x05, 7, 2, 0x1f);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x19, 9, 5, 0x1f);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x0b, 15, 9, 0x3c);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0b, 1, 0, 0x03);
|
|
|
|
pr_info("end_1.1.5\n");
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_2_1(struct rtpcs_ctrl *ctrl,
|
|
u32 sds_num)
|
|
{
|
|
pr_info("start_1.2.1 ForegroundOffsetCal_Manual\n");
|
|
|
|
/* Gray config endis to 1 */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x02, 2, 2, 0x01);
|
|
|
|
/* ForegroundOffsetCal_Manual(auto mode) */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x01, 14, 14, 0x00);
|
|
|
|
pr_info("end_1.2.1");
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_2_2(struct rtpcs_ctrl *ctrl,
|
|
int sds_num)
|
|
{
|
|
/* Force Rx-Run = 0 */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 8, 8, 0x0);
|
|
|
|
rtpcs_930x_sds_rx_reset(ctrl, sds_num, PHY_INTERFACE_MODE_10GBASER);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_2_3(struct rtpcs_ctrl *ctrl,
|
|
int sds_num)
|
|
{
|
|
u32 fgcal_binary, fgcal_gray;
|
|
u32 offset_range;
|
|
|
|
pr_info("start_1.2.3 Foreground Calibration\n");
|
|
|
|
while (1) {
|
|
if (!(sds_num % 2))
|
|
rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f);
|
|
else
|
|
rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31);
|
|
|
|
/* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1);
|
|
/* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20);
|
|
/* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 1 1] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0xf);
|
|
/* ##FGCAL read gray */
|
|
fgcal_gray = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 0);
|
|
/* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 1 0] */
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0xe);
|
|
/* ##FGCAL read binary */
|
|
fgcal_binary = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 0);
|
|
|
|
pr_info("%s: fgcal_gray: %d, fgcal_binary %d\n",
|
|
__func__, fgcal_gray, fgcal_binary);
|
|
|
|
offset_range = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x15, 15, 14);
|
|
|
|
if (fgcal_binary <= 60 && fgcal_binary >= 3)
|
|
break;
|
|
|
|
if (offset_range == 3) {
|
|
pr_info("%s: Foreground Calibration result marginal!", __func__);
|
|
break;
|
|
}
|
|
|
|
offset_range++;
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 15, 14, offset_range);
|
|
rtpcs_930x_sds_do_rx_calibration_2_2(ctrl, sds_num);
|
|
}
|
|
pr_info("%s: end_1.2.3\n", __func__);
|
|
}
|
|
|
|
__always_unused
|
|
static void rtpcs_930x_sds_do_rx_calibration_2(struct rtpcs_ctrl *ctrl, int sds)
|
|
{
|
|
rtpcs_930x_sds_rx_reset(ctrl, sds, PHY_INTERFACE_MODE_10GBASER);
|
|
rtpcs_930x_sds_do_rx_calibration_2_1(ctrl, sds);
|
|
rtpcs_930x_sds_do_rx_calibration_2_2(ctrl, sds);
|
|
rtpcs_930x_sds_do_rx_calibration_2_3(ctrl, sds);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_3_1(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
pr_info("start_1.3.1");
|
|
|
|
/* ##1.3.1 */
|
|
if (phy_mode != PHY_INTERFACE_MODE_10GBASER &&
|
|
phy_mode != PHY_INTERFACE_MODE_1000BASEX &&
|
|
phy_mode != PHY_INTERFACE_MODE_SGMII)
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0xc, 8, 8, 0);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, 0x0);
|
|
rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, false, 0);
|
|
|
|
pr_info("end_1.3.1");
|
|
}
|
|
|
|
static void rtpcs_930x_sds_rxcal_3_2(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
u32 sum10 = 0, avg10, int10;
|
|
int dac_long_cable_offset;
|
|
bool eq_hold_enabled;
|
|
int i;
|
|
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER ||
|
|
phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
|
|
phy_mode == PHY_INTERFACE_MODE_SGMII) {
|
|
/* rtl9300_rxCaliConf_serdes_myParam */
|
|
dac_long_cable_offset = 3;
|
|
eq_hold_enabled = true;
|
|
} else {
|
|
/* rtl9300_rxCaliConf_phy_myParam */
|
|
dac_long_cable_offset = 0;
|
|
eq_hold_enabled = false;
|
|
}
|
|
|
|
if (phy_mode != PHY_INTERFACE_MODE_10GBASER)
|
|
pr_warn("%s: LEQ only valid for 10GR!\n", __func__);
|
|
|
|
pr_info("start_1.3.2");
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
sum10 += rtpcs_930x_sds_rxcal_leq_read(ctrl, sds_num);
|
|
mdelay(10);
|
|
}
|
|
|
|
avg10 = (sum10 / 10) + (((sum10 % 10) >= 5) ? 1 : 0);
|
|
int10 = sum10 / 10;
|
|
|
|
pr_info("sum10:%u, avg10:%u, int10:%u", sum10, avg10, int10);
|
|
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER ||
|
|
phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
|
|
phy_mode == PHY_INTERFACE_MODE_SGMII) {
|
|
if (dac_long_cable_offset) {
|
|
rtpcs_930x_sds_rxcal_leq_offset_manual(ctrl, sds_num, 1,
|
|
dac_long_cable_offset);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7,
|
|
eq_hold_enabled);
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER)
|
|
rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num,
|
|
true, avg10);
|
|
} else {
|
|
if (sum10 >= 5) {
|
|
rtpcs_930x_sds_rxcal_leq_offset_manual(ctrl, sds_num, 1, 3);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, 0x1);
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER)
|
|
rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, true, avg10);
|
|
} else {
|
|
rtpcs_930x_sds_rxcal_leq_offset_manual(ctrl, sds_num, 1, 0);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, 0x1);
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER)
|
|
rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, true, avg10);
|
|
}
|
|
}
|
|
}
|
|
|
|
pr_info("Sds:%u LEQ = %u", sds_num, rtpcs_930x_sds_rxcal_leq_read(ctrl, sds_num));
|
|
|
|
pr_info("end_1.3.2");
|
|
}
|
|
|
|
__always_unused
|
|
static void rtpcs_930x_sds_do_rx_calibration_3(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
rtpcs_930x_sds_rxcal_3_1(ctrl, sds_num, phy_mode);
|
|
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER ||
|
|
phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
|
|
phy_mode == PHY_INTERFACE_MODE_SGMII)
|
|
rtpcs_930x_sds_rxcal_3_2(ctrl, sds_num, phy_mode);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_4_1(struct rtpcs_ctrl *ctrl, int sds_num)
|
|
{
|
|
u32 vth_list[2] = {0, 0};
|
|
u32 tap0_list[4] = {0, 0, 0, 0};
|
|
|
|
pr_info("start_1.4.1");
|
|
|
|
/* ##1.4.1 */
|
|
rtpcs_930x_sds_rxcal_vth_manual(ctrl, sds_num, false, vth_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 0, false, tap0_list);
|
|
mdelay(200);
|
|
|
|
pr_info("end_1.4.1");
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_4_2(struct rtpcs_ctrl *ctrl, u32 sds_num)
|
|
{
|
|
u32 vth_list[2];
|
|
u32 tap_list[4];
|
|
|
|
pr_info("start_1.4.2");
|
|
|
|
rtpcs_930x_sds_rxcal_vth_get(ctrl, sds_num, vth_list);
|
|
rtpcs_930x_sds_rxcal_vth_manual(ctrl, sds_num, true, vth_list);
|
|
|
|
mdelay(100);
|
|
|
|
rtpcs_930x_sds_rxcal_tap_get(ctrl, sds_num, 0, tap_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 0, true, tap_list);
|
|
|
|
pr_info("end_1.4.2");
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_4(struct rtpcs_ctrl *ctrl, u32 sds_num)
|
|
{
|
|
rtpcs_930x_sds_do_rx_calibration_4_1(ctrl, sds_num);
|
|
rtpcs_930x_sds_do_rx_calibration_4_2(ctrl, sds_num);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_5_2(struct rtpcs_ctrl *ctrl, u32 sds_num)
|
|
{
|
|
u32 tap1_list[4] = {0};
|
|
u32 tap2_list[4] = {0};
|
|
u32 tap3_list[4] = {0};
|
|
u32 tap4_list[4] = {0};
|
|
|
|
pr_info("start_1.5.2");
|
|
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 1, false, tap1_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 2, false, tap2_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 3, false, tap3_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 4, false, tap4_list);
|
|
|
|
mdelay(30);
|
|
|
|
pr_info("end_1.5.2");
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_5(struct rtpcs_ctrl *ctrl, u32 sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
if (phy_mode == PHY_INTERFACE_MODE_10GBASER) /* dfeTap1_4Enable true */
|
|
rtpcs_930x_sds_do_rx_calibration_5_2(ctrl, sds_num);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration_dfe_disable(struct rtpcs_ctrl *ctrl, u32 sds_num)
|
|
{
|
|
u32 tap1_list[4] = {0};
|
|
u32 tap2_list[4] = {0};
|
|
u32 tap3_list[4] = {0};
|
|
u32 tap4_list[4] = {0};
|
|
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 1, true, tap1_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 2, true, tap2_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 3, true, tap3_list);
|
|
rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 4, true, tap4_list);
|
|
|
|
mdelay(10);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_do_rx_calibration(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
u32 latch_sts;
|
|
|
|
rtpcs_930x_sds_do_rx_calibration_1(ctrl, sds, phy_mode);
|
|
rtpcs_930x_sds_do_rx_calibration_2(ctrl, sds);
|
|
rtpcs_930x_sds_do_rx_calibration_4(ctrl, sds);
|
|
rtpcs_930x_sds_do_rx_calibration_5(ctrl, sds, phy_mode);
|
|
mdelay(20);
|
|
|
|
/* Do this only for 10GR mode, SDS active in mode 0x1a */
|
|
if (rtpcs_sds_read_bits(ctrl, sds, 0x1f, 9, 11, 7) == RTL930X_SDS_MODE_10GBASER) {
|
|
pr_info("%s: SDS enabled\n", __func__);
|
|
latch_sts = rtpcs_sds_read_bits(ctrl, sds, 0x4, 1, 2, 2);
|
|
mdelay(1);
|
|
latch_sts = rtpcs_sds_read_bits(ctrl, sds, 0x4, 1, 2, 2);
|
|
if (latch_sts) {
|
|
rtpcs_930x_sds_do_rx_calibration_dfe_disable(ctrl, sds);
|
|
rtpcs_930x_sds_do_rx_calibration_4(ctrl, sds);
|
|
rtpcs_930x_sds_do_rx_calibration_5(ctrl, sds, phy_mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int rtpcs_930x_sds_sym_err_reset(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
switch (phy_mode) {
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
/* Read twice to clear */
|
|
rtpcs_sds_read(ctrl, sds_num, 5, 1);
|
|
rtpcs_sds_read(ctrl, sds_num, 5, 1);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x1, 24, 2, 0, 0);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x1, 3, 15, 8, 0);
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x1, 2, 15, 0, 0);
|
|
break;
|
|
|
|
default:
|
|
pr_info("%s unsupported phy mode\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u32 rtpcs_930x_sds_sym_err_get(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
u32 v = 0;
|
|
|
|
switch (phy_mode) {
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
v = rtpcs_sds_read(ctrl, sds_num, 5, 1);
|
|
return v & 0xff;
|
|
|
|
default:
|
|
pr_info("%s unsupported PHY-mode\n", __func__);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static int rtpcs_930x_sds_check_calibration(struct rtpcs_ctrl *ctrl, int sds_num,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
u32 errors1, errors2;
|
|
|
|
rtpcs_930x_sds_sym_err_reset(ctrl, sds_num, phy_mode);
|
|
rtpcs_930x_sds_sym_err_reset(ctrl, sds_num, phy_mode);
|
|
|
|
/* Count errors during 1ms */
|
|
errors1 = rtpcs_930x_sds_sym_err_get(ctrl, sds_num, phy_mode);
|
|
mdelay(1);
|
|
errors2 = rtpcs_930x_sds_sym_err_get(ctrl, sds_num, phy_mode);
|
|
|
|
switch (phy_mode) {
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
if ((errors2 - errors1 > 100) ||
|
|
(errors1 >= 0xffff00) || (errors2 >= 0xffff00)) {
|
|
pr_info("%s XSGMII error rate too high\n", __func__);
|
|
return 1;
|
|
}
|
|
break;
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
if (errors2 > 0) {
|
|
pr_info("%s: %s error rate too high\n", __func__, phy_modes(phy_mode));
|
|
return 1;
|
|
}
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rtpcs_930x_phy_enable_10g_1g(struct rtpcs_ctrl *ctrl, int sds_num)
|
|
{
|
|
u32 v;
|
|
|
|
/* Enable 1GBit PHY */
|
|
v = rtpcs_sds_read(ctrl, sds_num, PHY_PAGE_2, MII_BMCR);
|
|
pr_info("%s 1gbit phy: %08x\n", __func__, v);
|
|
v &= ~BMCR_PDOWN;
|
|
rtpcs_sds_write(ctrl, sds_num, PHY_PAGE_2, MII_BMCR, v);
|
|
pr_info("%s 1gbit phy enabled: %08x\n", __func__, v);
|
|
|
|
/* Enable 10GBit PHY */
|
|
v = rtpcs_sds_read(ctrl, sds_num, PHY_PAGE_4, MII_BMCR);
|
|
pr_info("%s 10gbit phy: %08x\n", __func__, v);
|
|
v &= ~BMCR_PDOWN;
|
|
rtpcs_sds_write(ctrl, sds_num, PHY_PAGE_4, MII_BMCR, v);
|
|
pr_info("%s 10gbit phy after: %08x\n", __func__, v);
|
|
|
|
/* dal_longan_construct_mac_default_10gmedia_fiber */
|
|
v = rtpcs_sds_read(ctrl, sds_num, 0x1f, 11);
|
|
pr_info("%s set medium: %08x\n", __func__, v);
|
|
v |= BIT(1);
|
|
rtpcs_sds_write(ctrl, sds_num, 0x1f, 11, v);
|
|
pr_info("%s set medium after: %08x\n", __func__, v);
|
|
}
|
|
|
|
static int rtpcs_930x_sds_10g_idle(struct rtpcs_ctrl *ctrl, int sds_num)
|
|
{
|
|
bool busy;
|
|
int i = 0;
|
|
|
|
do {
|
|
if (sds_num % 2) {
|
|
rtpcs_sds_write_bits(ctrl, sds_num - 1, 0x1f, 0x2, 15, 0, 53);
|
|
busy = !!rtpcs_sds_read_bits(ctrl, sds_num - 1, 0x1f, 0x14, 1, 1);
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds_num, 0x1f, 0x2, 15, 0, 53);
|
|
busy = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 0, 0);
|
|
}
|
|
i++;
|
|
} while (busy && i < 100);
|
|
|
|
if (i < 100)
|
|
return 0;
|
|
|
|
pr_warn("%s WARNING: Waiting for RX idle timed out, SDS %d\n", __func__, sds_num);
|
|
return -EIO;
|
|
}
|
|
|
|
static int rtpcs_930x_sds_set_polarity(struct rtpcs_ctrl *ctrl, u32 sds,
|
|
bool tx_inv, bool rx_inv)
|
|
{
|
|
u8 rx_val = rx_inv ? 1 : 0;
|
|
u8 tx_val = tx_inv ? 1 : 0;
|
|
u32 val;
|
|
int ret;
|
|
|
|
/* 10GR */
|
|
val = (tx_val << 1) | rx_val;
|
|
ret = rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x2, 14, 13, val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* 1G */
|
|
val = (rx_val << 1) | tx_val;
|
|
return rtpcs_sds_write_bits(ctrl, sds, 0x0, 0x0, 9, 8, val);
|
|
}
|
|
|
|
static const sds_config rtpcs_930x_sds_cfg_10gr_even[] = {
|
|
/* 1G */
|
|
{0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, {0x21, 0x03, 0x8206},
|
|
{0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F},
|
|
{0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000},
|
|
{0x21, 0x0F, 0x0008}, {0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020},
|
|
{0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, {0x24, 0x0F, 0xFFDF},
|
|
{0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311},
|
|
{0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001},
|
|
{0x24, 0x1C, 0x0400}, {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017},
|
|
{0x25, 0x03, 0xFFDF}, {0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100},
|
|
{0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F},
|
|
{0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020},
|
|
{0x25, 0x11, 0x8840}, {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88},
|
|
{0x2B, 0x19, 0x4902}, {0x2B, 0x1D, 0x2501}, {0x2D, 0x13, 0x0050},
|
|
{0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1D, 0x2641},
|
|
{0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, {0x2F, 0x19, 0x4902},
|
|
{0x2F, 0x1D, 0x66E1},
|
|
/* 3.125G */
|
|
{0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000},
|
|
{0x28, 0x0B, 0x1892}, {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4},
|
|
{0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, {0x28, 0x16, 0x00C9},
|
|
{0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400},
|
|
{0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF},
|
|
{0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001},
|
|
{0x29, 0x09, 0xFFD4}, {0x29, 0x0A, 0x7C2F}, {0x29, 0x0E, 0x003F},
|
|
{0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840},
|
|
/* 10G */
|
|
{0x06, 0x0D, 0x0F00}, {0x06, 0x00, 0x0000}, {0x06, 0x01, 0xC800},
|
|
{0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010},
|
|
{0x21, 0x07, 0xF09F}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009},
|
|
{0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, {0x2E, 0x00, 0xA668},
|
|
{0x2E, 0x02, 0xD020}, {0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892},
|
|
{0x2E, 0x0F, 0xFFDF}, {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0044},
|
|
{0x2E, 0x13, 0x027F}, {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100},
|
|
{0x2E, 0x1A, 0x0001}, {0x2E, 0x1C, 0x0400}, {0x2F, 0x01, 0x0300},
|
|
{0x2F, 0x02, 0x1217}, {0x2F, 0x03, 0xFFDF}, {0x2F, 0x05, 0x7F7C},
|
|
{0x2F, 0x07, 0x80C4}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4},
|
|
{0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121},
|
|
{0x2F, 0x10, 0x0020}, {0x2F, 0x11, 0x8840}, {0x2F, 0x14, 0xE008},
|
|
{0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, {0x2B, 0x19, 0x4902},
|
|
{0x2B, 0x1D, 0x2501}, {0x2D, 0x13, 0x0050}, {0x2D, 0x17, 0x4109},
|
|
{0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1C, 0x1109},
|
|
{0x2D, 0x1D, 0x2641}, {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88},
|
|
{0x2F, 0x19, 0x4902}, {0x2F, 0x1D, 0x76E1},
|
|
};
|
|
|
|
static const sds_config rtpcs_930x_sds_cfg_10gr_odd[] = {
|
|
/* 1G */
|
|
{0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, {0x21, 0x03, 0x8206},
|
|
{0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003},
|
|
{0x21, 0x0B, 0x0005}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009},
|
|
{0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, {0x24, 0x00, 0x0668},
|
|
{0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892},
|
|
{0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F},
|
|
{0x24, 0x14, 0x1311}, {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100},
|
|
{0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400}, {0x25, 0x00, 0x820F},
|
|
{0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF},
|
|
{0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001},
|
|
{0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F}, {0x25, 0x0E, 0x003F},
|
|
{0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, {0x25, 0x11, 0x8840},
|
|
{0x2B, 0x13, 0x3D87}, {0x2B, 0x14, 0x3108}, {0x2D, 0x13, 0x3C87},
|
|
{0x2D, 0x14, 0x1808},
|
|
/* 3.125G */
|
|
{0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000},
|
|
{0x28, 0x0B, 0x1892}, {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4},
|
|
{0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, {0x28, 0x16, 0x00C9},
|
|
{0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400},
|
|
{0x29, 0x00, 0x820F}, {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017},
|
|
{0x29, 0x03, 0xFFDF}, {0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100},
|
|
{0x29, 0x08, 0x0001}, {0x29, 0x0A, 0x7C2F}, {0x29, 0x0E, 0x003F},
|
|
{0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840},
|
|
/* 10G */
|
|
{0x06, 0x0D, 0x0F00}, {0x06, 0x00, 0x0000}, {0x06, 0x01, 0xC800},
|
|
{0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010},
|
|
{0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003}, {0x21, 0x0B, 0x0005},
|
|
{0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000},
|
|
{0x21, 0x0F, 0x0008}, {0x2E, 0x00, 0xA668}, {0x2E, 0x02, 0xD020},
|
|
{0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892}, {0x2E, 0x0F, 0xFFDF},
|
|
{0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0044}, {0x2E, 0x13, 0x027F},
|
|
{0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, {0x2E, 0x1A, 0x0001},
|
|
{0x2E, 0x1C, 0x0400}, {0x2F, 0x00, 0x820F}, {0x2F, 0x01, 0x0300},
|
|
{0x2F, 0x02, 0x1217}, {0x2F, 0x03, 0xFFDF}, {0x2F, 0x05, 0x7F7C},
|
|
{0x2F, 0x07, 0x80C4}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4},
|
|
{0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121},
|
|
{0x2F, 0x10, 0x0020}, {0x2F, 0x11, 0x8840}, {0x2B, 0x13, 0x3D87},
|
|
{0x2B, 0x14, 0x3108}, {0x2D, 0x13, 0x3C87}, {0x2D, 0x14, 0x1808},
|
|
};
|
|
|
|
static const sds_config rtpcs_930x_sds_cfg_10g_2500bx_even[] = {
|
|
{0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100},
|
|
{0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F},
|
|
{0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008},
|
|
{0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892},
|
|
{0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311},
|
|
{0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400},
|
|
{0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF}, {0x25, 0x05, 0x7F7C},
|
|
{0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F},
|
|
{0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, {0x25, 0x11, 0x8840},
|
|
{0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, {0x28, 0x0B, 0x1892},
|
|
{0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311},
|
|
{0x28, 0x16, 0x00C9}, {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400},
|
|
{0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF}, {0x29, 0x05, 0x7F7C},
|
|
{0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001}, {0x29, 0x09, 0xFFD4}, {0x29, 0x0A, 0x7C2F},
|
|
{0x29, 0x0E, 0x003F}, {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840},
|
|
{0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, {0x2B, 0x19, 0x4902}, {0x2B, 0x1D, 0x2501},
|
|
{0x2D, 0x13, 0x0050}, {0x2D, 0x18, 0x8E88}, {0x2D, 0x17, 0x4109}, {0x2D, 0x19, 0x4902},
|
|
{0x2D, 0x1C, 0x1109}, {0x2D, 0x1D, 0x2641},
|
|
{0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, {0x2F, 0x19, 0x4902}, {0x2F, 0x1D, 0x66E1},
|
|
};
|
|
|
|
static const sds_config rtpcs_930x_sds_cfg_10g_2500bx_odd[] = {
|
|
{0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100},
|
|
{0x21, 0x03, 0x8206}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003},
|
|
{0x21, 0x0B, 0x0005}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000},
|
|
{0x21, 0x0F, 0x0008},
|
|
{0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892},
|
|
{0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311},
|
|
{0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400},
|
|
{0x25, 0x00, 0x820F}, {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF},
|
|
{0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4},
|
|
{0x25, 0x0A, 0x7C2F}, {0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020},
|
|
{0x25, 0x11, 0x8840},
|
|
{0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, {0x28, 0x0B, 0x1892},
|
|
{0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311},
|
|
{0x28, 0x16, 0x00C9}, {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400},
|
|
{0x29, 0x00, 0x820F}, {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF},
|
|
{0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001}, {0x29, 0x0A, 0x7C2F},
|
|
{0x29, 0x0E, 0x003F}, {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840},
|
|
{0x2B, 0x13, 0x3D87}, {0x2B, 0x14, 0x3108},
|
|
{0x2D, 0x13, 0x3C87}, {0x2D, 0x14, 0x1808},
|
|
};
|
|
|
|
static void rtpcs_930x_sds_usxgmii_config(struct rtpcs_ctrl *ctrl, int sds,
|
|
int nway_en, u32 opcode, u32 am_period,
|
|
u32 all_am_markers, u32 an_table,
|
|
u32 sync_bit)
|
|
{
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 0, 0, nway_en);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 1, 1, nway_en);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 2, 2, nway_en);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 3, 3, nway_en);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x12, 15, 0, am_period);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x13, 7, 0, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x13, 15, 8, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x14, 7, 0, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x14, 15, 8, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x15, 7, 0, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x15, 15, 8, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x16, 7, 0, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x16, 15, 8, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x17, 7, 0, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x17, 15, 8, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x18, 7, 0, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x18, 15, 8, all_am_markers);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x10, 7, 0, opcode);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0xe, 10, 10, an_table);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x1d, 11, 10, sync_bit);
|
|
}
|
|
|
|
static void rtpcs_930x_sds_patch(struct rtpcs_ctrl *ctrl, int sds, phy_interface_t mode)
|
|
{
|
|
const bool even_sds = ((sds & 1) == 0);
|
|
const sds_config *config;
|
|
size_t count;
|
|
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
if (even_sds) {
|
|
config = rtpcs_930x_sds_cfg_10gr_even;
|
|
count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10gr_even);
|
|
} else {
|
|
config = rtpcs_930x_sds_cfg_10gr_odd;
|
|
count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10gr_odd);
|
|
}
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
if (even_sds) {
|
|
config = rtpcs_930x_sds_cfg_10g_2500bx_even;
|
|
count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10g_2500bx_even);
|
|
} else {
|
|
config = rtpcs_930x_sds_cfg_10g_2500bx_odd;
|
|
count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10g_2500bx_odd);
|
|
}
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
return;
|
|
|
|
default:
|
|
pr_warn("%s: unsupported mode %s on serdes %d\n", __func__, phy_modes(mode), sds);
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
rtpcs_sds_write(ctrl, sds, config[i].page, config[i].reg, config[i].data);
|
|
|
|
if (mode == PHY_INTERFACE_MODE_10G_QXGMII) {
|
|
/* Default configuration */
|
|
rtpcs_930x_sds_usxgmii_config(ctrl, sds, 1, 0xaa, 0x5078, 0, 1, 0x1);
|
|
}
|
|
}
|
|
|
|
__always_unused
|
|
static int rtpcs_930x_sds_cmu_band_get(struct rtpcs_ctrl *ctrl, int sds)
|
|
{
|
|
u32 page;
|
|
u32 en;
|
|
u32 cmu_band;
|
|
|
|
/* page = rtl9300_sds_cmu_page_get(sds); */
|
|
page = 0x25; /* 10GR and 1000BX */
|
|
sds = (sds % 2) ? (sds - 1) : (sds);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x1c, 15, 15, 1);
|
|
rtpcs_sds_write_bits(ctrl, sds + 1, page, 0x1c, 15, 15, 1);
|
|
|
|
en = rtpcs_sds_read_bits(ctrl, sds, page, 27, 1, 1);
|
|
if (!en) { /* Auto mode */
|
|
rtpcs_sds_write(ctrl, sds, 0x1f, 0x02, 31);
|
|
|
|
cmu_band = rtpcs_sds_read_bits(ctrl, sds, 0x1f, 0x15, 5, 1);
|
|
} else {
|
|
cmu_band = rtpcs_sds_read_bits(ctrl, sds, page, 30, 4, 0);
|
|
}
|
|
|
|
return cmu_band;
|
|
}
|
|
|
|
static int rtpcs_930x_setup_serdes(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
int calib_tries = 0;
|
|
|
|
if (sds < 0 || sds > 11)
|
|
return -EINVAL;
|
|
|
|
/* Rely on setup from U-boot for some modes, e.g. USXGMII */
|
|
switch (phy_mode) {
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Turn Off Serdes */
|
|
rtpcs_930x_sds_set(ctrl, sds, RTL930X_SDS_OFF);
|
|
|
|
/* Apply serdes patches */
|
|
rtpcs_930x_sds_patch(ctrl, sds, phy_mode);
|
|
|
|
/* Maybe use dal_longan_sds_init */
|
|
|
|
/* dal_longan_construct_serdesConfig_init */ /* Serdes Construct */
|
|
rtpcs_930x_phy_enable_10g_1g(ctrl, sds);
|
|
|
|
/* ----> dal_longan_sds_mode_set */
|
|
pr_info("%s: Configuring RTL9300 SERDES %d\n", __func__, sds);
|
|
|
|
/* Set SDS polarity */
|
|
rtpcs_930x_sds_set_polarity(ctrl, sds, ctrl->tx_pol_inv[sds],
|
|
ctrl->rx_pol_inv[sds]);
|
|
|
|
/* Enable SDS in desired mode */
|
|
rtpcs_930x_sds_mode_set(ctrl, sds, phy_mode);
|
|
|
|
/* Enable Fiber RX */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x20, 2, 12, 12, 0);
|
|
|
|
/* Calibrate SerDes receiver in loopback mode */
|
|
rtpcs_930x_sds_10g_idle(ctrl, sds);
|
|
do {
|
|
rtpcs_930x_sds_do_rx_calibration(ctrl, sds, phy_mode);
|
|
calib_tries++;
|
|
mdelay(50);
|
|
} while (rtpcs_930x_sds_check_calibration(ctrl, sds, phy_mode) && calib_tries < 3);
|
|
if (calib_tries >= 3)
|
|
pr_warn("%s: SerDes RX calibration failed\n", __func__);
|
|
|
|
/* Leave loopback mode */
|
|
rtpcs_930x_sds_tx_config(ctrl, sds, phy_mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* RTL931X */
|
|
|
|
static void rtpcs_931x_sds_reset(struct rtpcs_ctrl *ctrl, u32 sds)
|
|
{
|
|
u32 o, v, o_mode;
|
|
int shift = ((sds & 0x3) << 3);
|
|
|
|
/* TODO: We need to lock this! */
|
|
|
|
regmap_read(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, &o);
|
|
v = o | BIT(sds);
|
|
regmap_write(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, v);
|
|
|
|
regmap_read(ctrl->map, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2), &o_mode);
|
|
v = BIT(7) | 0x1F;
|
|
regmap_write_bits(ctrl->map, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2),
|
|
0xff << shift, v << shift);
|
|
regmap_write(ctrl->map, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2), o_mode);
|
|
|
|
regmap_write(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, o);
|
|
}
|
|
|
|
static void rtpcs_931x_sds_disable(struct rtpcs_ctrl *ctrl, u32 sds)
|
|
{
|
|
regmap_write(ctrl->map, RTL931X_SERDES_MODE_CTRL + (sds >> 2) * 4, 0x9f);
|
|
}
|
|
|
|
static void rtpcs_931x_sds_symerr_clear(struct rtpcs_ctrl *ctrl, u32 sds,
|
|
phy_interface_t mode)
|
|
{
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_NA:
|
|
break;
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
for (int i = 0; i < 4; ++i) {
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 24, 2, 0, i);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 3, 15, 8, 0x0);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 2, 15, 0, 0x0);
|
|
}
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 24, 2, 0, i);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 3, 15, 8, 0x0);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 2, 15, 0, 0x0);
|
|
}
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 0, 15, 0, 0x0);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 1, 15, 8, 0x0);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 0, 15, 0, 0x0);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 1, 15, 8, 0x0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
__always_unused
|
|
static void rtpcs_931x_sds_fiber_disable(struct rtpcs_ctrl *ctrl, u32 sds)
|
|
{
|
|
u32 v = 0x3F;
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1F, 0x9, 11, 6, v);
|
|
}
|
|
|
|
static void rtpcs_931x_sds_fiber_mode_set(struct rtpcs_ctrl *ctrl, u32 sds,
|
|
phy_interface_t mode)
|
|
{
|
|
u32 val;
|
|
|
|
/* clear symbol error count before changing mode */
|
|
rtpcs_931x_sds_symerr_clear(ctrl, sds, mode);
|
|
|
|
rtpcs_931x_sds_disable(ctrl, sds);
|
|
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
val = 0x5;
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
/* serdes mode FIBER1G */
|
|
val = 0x9;
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
case PHY_INTERFACE_MODE_10GKR:
|
|
val = 0x35;
|
|
break;
|
|
/* case MII_10GR1000BX_AUTO:
|
|
val = 0x39;
|
|
break; */
|
|
|
|
case PHY_INTERFACE_MODE_USXGMII:
|
|
val = 0x1B;
|
|
break;
|
|
default:
|
|
val = 0x25;
|
|
}
|
|
|
|
pr_info("%s writing analog SerDes Mode value %02x\n", __func__, val);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1F, 0x9, 11, 6, val);
|
|
}
|
|
|
|
static int rtpcs_931x_sds_cmu_page_get(phy_interface_t mode)
|
|
{
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_1000BASEX: /* MII_1000BX_FIBER / 100BX_FIBER / 1000BX100BX_AUTO */
|
|
return 0x24;
|
|
case PHY_INTERFACE_MODE_2500BASEX: /* MII_2500Base_X: */
|
|
return 0x28;
|
|
/* case MII_HISGMII_5G: */
|
|
/* return 0x2a; */
|
|
case PHY_INTERFACE_MODE_QSGMII:
|
|
return 0x2a; /* Code also has 0x34 */
|
|
case PHY_INTERFACE_MODE_XAUI: /* MII_RXAUI_LITE: */
|
|
return 0x2c;
|
|
case PHY_INTERFACE_MODE_XGMII: /* MII_XSGMII */
|
|
case PHY_INTERFACE_MODE_10GKR:
|
|
case PHY_INTERFACE_MODE_10GBASER: /* MII_10GR */
|
|
return 0x2e;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void rtpcs_931x_sds_cmu_type_set(struct rtpcs_ctrl *ctrl, u32 sds,
|
|
phy_interface_t mode, int chiptype)
|
|
{
|
|
int cmu_type = 0; /* Clock Management Unit */
|
|
u32 cmu_page = 0;
|
|
u32 frc_cmu_spd;
|
|
u32 evenSds;
|
|
u32 lane, frc_lc_mode_bitnum, frc_lc_mode_val_bitnum;
|
|
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_NA:
|
|
case PHY_INTERFACE_MODE_10GKR:
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
case PHY_INTERFACE_MODE_USXGMII:
|
|
return;
|
|
|
|
/* case MII_10GR1000BX_AUTO:
|
|
if (chiptype)
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x24, 0xd, 14, 14, 0);
|
|
return; */
|
|
|
|
case PHY_INTERFACE_MODE_QSGMII:
|
|
cmu_type = 1;
|
|
frc_cmu_spd = 0;
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
cmu_type = 1;
|
|
frc_cmu_spd = 0;
|
|
break;
|
|
|
|
/* case MII_1000BX100BX_AUTO:
|
|
cmu_type = 1;
|
|
frc_cmu_spd = 0;
|
|
break; */
|
|
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
cmu_type = 1;
|
|
frc_cmu_spd = 0;
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
cmu_type = 1;
|
|
frc_cmu_spd = 1;
|
|
break;
|
|
|
|
default:
|
|
pr_info("SerDes %d mode is invalid\n", sds);
|
|
return;
|
|
}
|
|
|
|
if (cmu_type == 1)
|
|
cmu_page = rtpcs_931x_sds_cmu_page_get(mode);
|
|
|
|
lane = sds % 2;
|
|
|
|
if (!lane) {
|
|
frc_lc_mode_bitnum = 4;
|
|
frc_lc_mode_val_bitnum = 5;
|
|
} else {
|
|
frc_lc_mode_bitnum = 6;
|
|
frc_lc_mode_val_bitnum = 7;
|
|
}
|
|
|
|
evenSds = sds - lane;
|
|
|
|
pr_info("%s: cmu_type %0d cmu_page %x frc_cmu_spd %d lane %d sds %d\n",
|
|
__func__, cmu_type, cmu_page, frc_cmu_spd, lane, sds);
|
|
|
|
if (cmu_type == 1) {
|
|
pr_info("%s A CMU page 0x28 0x7 %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x28, 0x7));
|
|
rtpcs_sds_write_bits(ctrl, sds, cmu_page, 0x7, 15, 15, 0);
|
|
pr_info("%s B CMU page 0x28 0x7 %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x28, 0x7));
|
|
if (chiptype)
|
|
rtpcs_sds_write_bits(ctrl, sds, cmu_page, 0xd, 14, 14, 0);
|
|
|
|
rtpcs_sds_write_bits(ctrl, evenSds, 0x20, 0x12, 3, 2, 0x3);
|
|
rtpcs_sds_write_bits(ctrl, evenSds, 0x20, 0x12, frc_lc_mode_bitnum, frc_lc_mode_bitnum, 1);
|
|
rtpcs_sds_write_bits(ctrl, evenSds, 0x20, 0x12, frc_lc_mode_val_bitnum, frc_lc_mode_val_bitnum, 0);
|
|
rtpcs_sds_write_bits(ctrl, evenSds, 0x20, 0x12, 12, 12, 1);
|
|
rtpcs_sds_write_bits(ctrl, evenSds, 0x20, 0x12, 15, 13, frc_cmu_spd);
|
|
}
|
|
|
|
pr_info("%s CMU page 0x28 0x7 %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x28, 0x7));
|
|
}
|
|
|
|
static void rtpcs_931x_sds_rx_reset(struct rtpcs_ctrl *ctrl, u32 sds)
|
|
{
|
|
if (sds < 2)
|
|
return;
|
|
|
|
rtpcs_sds_write(ctrl, sds, 0x2e, 0x12, 0x2740);
|
|
rtpcs_sds_write(ctrl, sds, 0x2f, 0x0, 0x0);
|
|
rtpcs_sds_write(ctrl, sds, 0x2f, 0x2, 0x2010);
|
|
rtpcs_sds_write(ctrl, sds, 0x20, 0x0, 0xc10);
|
|
|
|
rtpcs_sds_write(ctrl, sds, 0x2e, 0x12, 0x27c0);
|
|
rtpcs_sds_write(ctrl, sds, 0x2f, 0x0, 0xc000);
|
|
rtpcs_sds_write(ctrl, sds, 0x2f, 0x2, 0x6010);
|
|
rtpcs_sds_write(ctrl, sds, 0x20, 0x0, 0xc30);
|
|
|
|
mdelay(50);
|
|
}
|
|
|
|
static void rtpcs_931x_sds_mii_mode_set(struct rtpcs_ctrl *ctrl, u32 sds,
|
|
phy_interface_t mode)
|
|
{
|
|
u32 val;
|
|
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_QSGMII:
|
|
val = 0x6;
|
|
break;
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
val = 0x10; /* serdes mode XSGMII */
|
|
break;
|
|
case PHY_INTERFACE_MODE_USXGMII:
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
val = 0xD;
|
|
break;
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
val = 0x2;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
val |= (1 << 7);
|
|
|
|
regmap_write(ctrl->map, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2), val);
|
|
}
|
|
|
|
static int rtpcs_931x_sds_cmu_band_set(struct rtpcs_ctrl *ctrl, int sds,
|
|
bool enable, u32 band,
|
|
phy_interface_t mode)
|
|
{
|
|
int page = rtpcs_931x_sds_cmu_page_get(mode);
|
|
|
|
sds -= (sds % 2);
|
|
sds = sds & ~1;
|
|
page += 1;
|
|
|
|
if (enable) {
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x7, 13, 13, 0);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x7, 11, 11, 0);
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x7, 13, 13, 0);
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x7, 11, 11, 0);
|
|
}
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x7, 4, 0, band);
|
|
|
|
rtpcs_931x_sds_reset(ctrl, sds);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rtpcs_931x_sds_cmu_band_get(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t mode)
|
|
{
|
|
int page = rtpcs_931x_sds_cmu_page_get(mode);
|
|
u32 band;
|
|
|
|
sds -= (sds % 2);
|
|
page += 1;
|
|
rtpcs_sds_write(ctrl, sds, 0x1f, 0x02, 73);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, page, 0x5, 15, 15, 1);
|
|
band = rtpcs_sds_read_bits(ctrl, sds, 0x1f, 0x15, 8, 3);
|
|
pr_info("%s band is: %d\n", __func__, band);
|
|
|
|
return band;
|
|
}
|
|
|
|
__always_unused
|
|
static int rtpcs_931x_sds_link_sts_get(struct rtpcs_ctrl *ctrl, u32 sds)
|
|
{
|
|
u32 sts, sts1, latch_sts, latch_sts1;
|
|
|
|
if (0) {
|
|
sts = rtpcs_sds_read_bits(ctrl, sds, 0x41, 29, 8, 0);
|
|
sts1 = rtpcs_sds_read_bits(ctrl, sds, 0x81, 29, 8, 0);
|
|
latch_sts = rtpcs_sds_read_bits(ctrl, sds, 0x41, 30, 8, 0);
|
|
latch_sts1 = rtpcs_sds_read_bits(ctrl, sds, 0x81, 30, 8, 0);
|
|
} else {
|
|
sts = rtpcs_sds_read_bits(ctrl, sds, 0x5, 0, 12, 12);
|
|
latch_sts = rtpcs_sds_read_bits(ctrl, sds, 0x4, 1, 2, 2);
|
|
latch_sts1 = rtpcs_sds_read_bits(ctrl, sds, 0x42, 1, 2, 2);
|
|
sts1 = rtpcs_sds_read_bits(ctrl, sds, 0x42, 1, 2, 2);
|
|
}
|
|
|
|
pr_info("%s: serdes %d sts %d, sts1 %d, latch_sts %d, latch_sts1 %d\n", __func__,
|
|
sds, sts, sts1, latch_sts, latch_sts1);
|
|
|
|
return sts1;
|
|
}
|
|
|
|
static int rtpcs_931x_sds_set_polarity(struct rtpcs_ctrl *ctrl, u32 sds,
|
|
bool tx_inv, bool rx_inv)
|
|
{
|
|
u8 rx_val = rx_inv ? 1 : 0;
|
|
u8 tx_val = tx_inv ? 1 : 0;
|
|
u32 val;
|
|
int ret;
|
|
|
|
/* 10gr_*_inv */
|
|
val = (tx_val << 1) | rx_val;
|
|
ret = rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x2, 14, 13, val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* xsg_*_inv */
|
|
val = (rx_val << 1) | tx_val;
|
|
ret = rtpcs_sds_write_bits(ctrl, sds, 0x40, 0x0, 9, 8, val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return rtpcs_sds_write_bits(ctrl, sds, 0x80, 0x0, 9, 8, val);
|
|
}
|
|
|
|
static sds_config sds_config_10p3125g_type1[] = {
|
|
{ 0x2E, 0x00, 0x0107 }, { 0x2E, 0x01, 0x01A3 }, { 0x2E, 0x02, 0x6A24 },
|
|
{ 0x2E, 0x03, 0xD10D }, { 0x2E, 0x04, 0x8000 }, { 0x2E, 0x05, 0xA17E },
|
|
{ 0x2E, 0x06, 0xE31D }, { 0x2E, 0x07, 0x800E }, { 0x2E, 0x08, 0x0294 },
|
|
{ 0x2E, 0x09, 0x0CE4 }, { 0x2E, 0x0A, 0x7FC8 }, { 0x2E, 0x0B, 0xE0E7 },
|
|
{ 0x2E, 0x0C, 0x0200 }, { 0x2E, 0x0D, 0xDF80 }, { 0x2E, 0x0E, 0x0000 },
|
|
{ 0x2E, 0x0F, 0x1FC2 }, { 0x2E, 0x10, 0x0C3F }, { 0x2E, 0x11, 0x0000 },
|
|
{ 0x2E, 0x12, 0x27C0 }, { 0x2E, 0x13, 0x7E1D }, { 0x2E, 0x14, 0x1300 },
|
|
{ 0x2E, 0x15, 0x003F }, { 0x2E, 0x16, 0xBE7F }, { 0x2E, 0x17, 0x0090 },
|
|
{ 0x2E, 0x18, 0x0000 }, { 0x2E, 0x19, 0x4000 }, { 0x2E, 0x1A, 0x0000 },
|
|
{ 0x2E, 0x1B, 0x8000 }, { 0x2E, 0x1C, 0x011F }, { 0x2E, 0x1D, 0x0000 },
|
|
{ 0x2E, 0x1E, 0xC8FF }, { 0x2E, 0x1F, 0x0000 }, { 0x2F, 0x00, 0xC000 },
|
|
{ 0x2F, 0x01, 0xF000 }, { 0x2F, 0x02, 0x6010 }, { 0x2F, 0x12, 0x0EE7 },
|
|
{ 0x2F, 0x13, 0x0000 }
|
|
};
|
|
|
|
static sds_config sds_config_10p3125g_cmu_type1[] = {
|
|
{ 0x2F, 0x03, 0x4210 }, { 0x2F, 0x04, 0x0000 }, { 0x2F, 0x05, 0x0019 },
|
|
{ 0x2F, 0x06, 0x18A6 }, { 0x2F, 0x07, 0x2990 }, { 0x2F, 0x08, 0xFFF4 },
|
|
{ 0x2F, 0x09, 0x1F08 }, { 0x2F, 0x0A, 0x0000 }, { 0x2F, 0x0B, 0x8000 },
|
|
{ 0x2F, 0x0C, 0x4224 }, { 0x2F, 0x0D, 0x0000 }, { 0x2F, 0x0E, 0x0000 },
|
|
{ 0x2F, 0x0F, 0xA470 }, { 0x2F, 0x10, 0x8000 }, { 0x2F, 0x11, 0x037B }
|
|
};
|
|
|
|
static int rtpcs_931x_setup_serdes(struct rtpcs_ctrl *ctrl, int sds,
|
|
phy_interface_t mode)
|
|
{
|
|
u32 board_sds_tx_type1[] = {
|
|
0x01c3, 0x01c3, 0x01c3, 0x01a3, 0x01a3, 0x01a3,
|
|
0x0143, 0x0143, 0x0143, 0x0143, 0x0163, 0x0163,
|
|
};
|
|
u32 board_sds_tx[] = {
|
|
0x1a00, 0x1a00, 0x0200, 0x0200, 0x0200, 0x0200,
|
|
0x01a3, 0x01a3, 0x01a3, 0x01a3, 0x01e3, 0x01e3
|
|
};
|
|
u32 board_sds_tx2[] = {
|
|
0x0dc0, 0x01c0, 0x0200, 0x0180, 0x0160, 0x0123,
|
|
0x0123, 0x0163, 0x01a3, 0x01a0, 0x01c3, 0x09c3,
|
|
};
|
|
u32 band, ori, model_info, val;
|
|
int chiptype = 0;
|
|
|
|
if (sds < 0 || sds > 13)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* TODO: USXGMII is currently the swiss army knife to declare 10G
|
|
* multi port PHYs. Real devices use other modes instead. Especially
|
|
*
|
|
* - RTL8224 is driven in 10G_QXGMII
|
|
* - RTL8218D/E are driven in (Realtek proprietary) XSGMII (10G SGMII)
|
|
*
|
|
* For now disable all USXGMII SerDes handling and rely on U-Boot setup.
|
|
*/
|
|
if (mode == PHY_INTERFACE_MODE_USXGMII)
|
|
return -ENOTSUPP;
|
|
|
|
pr_info("%s: set sds %d to mode %d\n", __func__, sds, mode);
|
|
val = rtpcs_sds_read_bits(ctrl, sds, 0x1F, 0x9, 11, 6);
|
|
|
|
pr_info("%s: fibermode %08X stored mode 0x%x", __func__,
|
|
rtpcs_sds_read(ctrl, sds, 0x1f, 0x9), val);
|
|
pr_info("%s: SGMII mode %08X in 0x24 0x9", __func__,
|
|
rtpcs_sds_read(ctrl, sds, 0x24, 0x9));
|
|
pr_info("%s: CMU mode %08X stored even SDS %d", __func__,
|
|
rtpcs_sds_read(ctrl, sds & ~1, 0x20, 0x12), sds & ~1);
|
|
pr_info("%s: serdes_mode_ctrl %08X", __func__, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2));
|
|
pr_info("%s CMU page 0x24 0x7 %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x24, 0x7));
|
|
pr_info("%s CMU page 0x26 0x7 %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x26, 0x7));
|
|
pr_info("%s CMU page 0x28 0x7 %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x28, 0x7));
|
|
pr_info("%s XSG page 0x0 0xe %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x40, 0xe));
|
|
pr_info("%s XSG2 page 0x0 0xe %08x\n", __func__, rtpcs_sds_read(ctrl, sds, 0x80, 0xe));
|
|
|
|
regmap_read(ctrl->map, RTL93XX_MODEL_NAME_INFO, &model_info);
|
|
if ((model_info >> 4) & 0x1) {
|
|
pr_info("detected chiptype 1\n");
|
|
chiptype = 1;
|
|
} else {
|
|
pr_info("detected chiptype 0\n");
|
|
}
|
|
|
|
pr_info("%s: 2.5gbit %08X", __func__,
|
|
rtpcs_sds_read(ctrl, sds, 0x41, 0x14));
|
|
|
|
regmap_read(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, &ori);
|
|
pr_info("%s: RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR 0x%08X\n", __func__, ori);
|
|
val = ori | (1 << sds);
|
|
regmap_write(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, val);
|
|
|
|
/* this was in rtl931x_phylink_mac_config in dsa/rtl83xx/dsa.c before */
|
|
band = rtpcs_931x_sds_cmu_band_get(ctrl, sds, mode);
|
|
|
|
switch (mode) {
|
|
case PHY_INTERFACE_MODE_NA:
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_XGMII: /* MII_XSGMII */
|
|
|
|
if (chiptype) {
|
|
/* fifo inv clk */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 0x1, 7, 4, 0xf);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 0x1, 3, 0, 0xf);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 0x1, 7, 4, 0xf);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x81, 0x1, 3, 0, 0xf);
|
|
}
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x40, 0xE, 12, 12, 1);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x80, 0xE, 12, 12, 1);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_USXGMII: /* MII_USXGMII_10GSXGMII/10GDXGMII/10GQXGMII: */
|
|
u32 op_code = 0x6003;
|
|
u32 evenSds;
|
|
|
|
if (chiptype) {
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x2, 12, 12, 1);
|
|
|
|
for (int i = 0; i < sizeof(sds_config_10p3125g_type1) / sizeof(sds_config); ++i) {
|
|
rtpcs_sds_write(ctrl, sds,
|
|
sds_config_10p3125g_type1[i].page - 0x4,
|
|
sds_config_10p3125g_type1[i].reg,
|
|
sds_config_10p3125g_type1[i].data);
|
|
}
|
|
|
|
evenSds = sds & ~1;
|
|
|
|
for (int i = 0; i < sizeof(sds_config_10p3125g_cmu_type1) / sizeof(sds_config); ++i) {
|
|
rtpcs_sds_write(ctrl, evenSds,
|
|
sds_config_10p3125g_cmu_type1[i].page - 0x4,
|
|
sds_config_10p3125g_cmu_type1[i].reg,
|
|
sds_config_10p3125g_cmu_type1[i].data);
|
|
}
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x2, 12, 12, 0);
|
|
} else {
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0xd, 6, 0, 0x0);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0xd, 7, 7, 0x1);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1c, 5, 0, 0x1E);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 11, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1f, 11, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x0, 11, 0, 0x00);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x1, 11, 0, 0x00);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0xf, 12, 6, 0x7F);
|
|
rtpcs_sds_write(ctrl, sds, 0x2f, 0x12, 0xaaa);
|
|
|
|
rtpcs_931x_sds_rx_reset(ctrl, sds);
|
|
|
|
rtpcs_sds_write(ctrl, sds, 0x7, 0x10, op_code);
|
|
rtpcs_sds_write(ctrl, sds, 0x6, 0x1d, 0x0480);
|
|
rtpcs_sds_write(ctrl, sds, 0x6, 0xe, 0x0400);
|
|
}
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_10GBASER: /* MII_10GR / MII_10GR1000BX_AUTO: */
|
|
/* configure 10GR fiber mode=1 */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0xb, 1, 1, 1);
|
|
|
|
/* init fiber_1g */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x43, 0x13, 15, 14, 0);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x42, 0x0, 12, 12, 1);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x42, 0x0, 6, 6, 1);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x42, 0x0, 13, 13, 0);
|
|
|
|
/* init auto */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1f, 13, 15, 0, 0x109e);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0x6, 14, 10, 0x8);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0x7, 10, 4, 0x7f);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_1000BASEX: /* MII_1000BX_FIBER */
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x43, 0x13, 15, 14, 0);
|
|
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x42, 0x0, 12, 12, 1);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x42, 0x0, 6, 6, 1);
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x42, 0x0, 13, 13, 0);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x24, 0x9, 15, 15, 0);
|
|
|
|
/* this was in rtl931x_phylink_mac_config in dsa/rtl83xx/dsa.c before */
|
|
band = rtpcs_931x_sds_cmu_band_set(ctrl, sds, true, 62, PHY_INTERFACE_MODE_SGMII);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
rtpcs_sds_write_bits(ctrl, sds, 0x41, 0x14, 8, 8, 1);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_QSGMII:
|
|
default:
|
|
pr_info("%s: PHY mode %s not supported by SerDes %d\n",
|
|
__func__, phy_modes(mode), sds);
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
rtpcs_931x_sds_cmu_type_set(ctrl, sds, mode, chiptype);
|
|
|
|
if (sds >= 2 && sds <= 13) {
|
|
if (chiptype)
|
|
rtpcs_sds_write(ctrl, sds, 0x2E, 0x1, board_sds_tx_type1[sds - 2]);
|
|
else {
|
|
val = 0xa0000;
|
|
regmap_write(ctrl->map, RTL93XX_CHIP_INFO, val);
|
|
regmap_read(ctrl->map, RTL93XX_CHIP_INFO, &val);
|
|
|
|
if (val & BIT(28)) /* consider 9311 etc. RTL9313_CHIP_ID == HWP_CHIP_ID(unit)) */
|
|
rtpcs_sds_write(ctrl, sds, 0x2E, 0x1, board_sds_tx2[sds - 2]);
|
|
else
|
|
rtpcs_sds_write(ctrl, sds, 0x2E, 0x1, board_sds_tx[sds - 2]);
|
|
|
|
val = 0;
|
|
regmap_write(ctrl->map, RTL93XX_CHIP_INFO, val);
|
|
}
|
|
}
|
|
|
|
rtpcs_931x_sds_set_polarity(ctrl, sds, ctrl->tx_pol_inv[sds],
|
|
ctrl->rx_pol_inv[sds]);
|
|
|
|
val = ori & ~BIT(sds);
|
|
regmap_write(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, val);
|
|
regmap_read(ctrl->map, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR, &val);
|
|
pr_debug("%s: RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR 0x%08X\n", __func__, val);
|
|
|
|
if (mode == PHY_INTERFACE_MODE_XGMII ||
|
|
mode == PHY_INTERFACE_MODE_QSGMII ||
|
|
mode == PHY_INTERFACE_MODE_SGMII ||
|
|
mode == PHY_INTERFACE_MODE_USXGMII) {
|
|
if (mode == PHY_INTERFACE_MODE_XGMII)
|
|
rtpcs_931x_sds_mii_mode_set(ctrl, sds, mode);
|
|
else
|
|
rtpcs_931x_sds_fiber_mode_set(ctrl, sds, mode);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Common functions */
|
|
|
|
static void rtpcs_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state)
|
|
{
|
|
struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs);
|
|
struct rtpcs_ctrl *ctrl = link->ctrl;
|
|
int port = link->port;
|
|
int linkup, speed;
|
|
|
|
state->link = 0;
|
|
state->speed = SPEED_UNKNOWN;
|
|
state->duplex = DUPLEX_UNKNOWN;
|
|
state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX);
|
|
|
|
/* Read MAC side link twice */
|
|
for (int i = 0; i < 2; i++)
|
|
linkup = rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_link_sts, port, port);
|
|
|
|
if (!linkup)
|
|
return;
|
|
|
|
state->link = 1;
|
|
state->duplex = rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_link_dup_sts, port, port);
|
|
|
|
speed = rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_link_spd_sts,
|
|
ctrl->cfg->mac_link_spd_bits * (port + 1) - 1,
|
|
ctrl->cfg->mac_link_spd_bits * port);
|
|
switch (speed) {
|
|
case RTPCS_SPEED_10:
|
|
state->speed = SPEED_10;
|
|
break;
|
|
case RTPCS_SPEED_100:
|
|
state->speed = SPEED_100;
|
|
break;
|
|
case RTPCS_SPEED_1000:
|
|
state->speed = SPEED_1000;
|
|
break;
|
|
case RTPCS_SPEED_10000:
|
|
case RTPCS_SPEED_10000_LEGACY:
|
|
/*
|
|
* The legacy mode is ok so far with minor inconsistencies. On RTL838x this flag
|
|
* is either 500M or 2G. It might be that MAC_GLITE_STS register tells more. On
|
|
* RTL839x this is either 500M or 10G. More info might be in MAC_LINK_500M_STS.
|
|
* Without support for the 500M modes simply resolve to 10G.
|
|
*/
|
|
state->speed = SPEED_10000;
|
|
break;
|
|
case RTPCS_SPEED_2500:
|
|
state->speed = SPEED_2500;
|
|
break;
|
|
case RTPCS_SPEED_5000:
|
|
state->speed = SPEED_5000;
|
|
break;
|
|
default:
|
|
dev_err(ctrl->dev, "unknown speed %d\n", speed);
|
|
}
|
|
|
|
if (rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_rx_pause_sts, port, port))
|
|
state->pause |= MLO_PAUSE_RX;
|
|
if (rtpcs_regmap_read_bits(ctrl, ctrl->cfg->mac_tx_pause_sts, port, port))
|
|
state->pause |= MLO_PAUSE_TX;
|
|
}
|
|
|
|
static void rtpcs_pcs_an_restart(struct phylink_pcs *pcs)
|
|
{
|
|
struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs);
|
|
struct rtpcs_ctrl *ctrl = link->ctrl;
|
|
|
|
dev_warn(ctrl->dev, "an_restart() for port %d and sds %d not yet implemented\n",
|
|
link->port, link->sds);
|
|
}
|
|
|
|
static int rtpcs_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
|
|
phy_interface_t interface, const unsigned long *advertising,
|
|
bool permit_pause_to_mac)
|
|
{
|
|
struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs);
|
|
struct rtpcs_ctrl *ctrl = link->ctrl;
|
|
int ret = 0;
|
|
|
|
if (link->sds < 0)
|
|
return 0;
|
|
|
|
/*
|
|
* TODO: This (or copies of this) will be the central function for configuring the
|
|
* link between PHY and SerDes. As of now a lot of the code is scattered throughout
|
|
* all the other Realtek drivers. Maybe some day this will live up to the expectations.
|
|
*/
|
|
|
|
dev_warn(ctrl->dev, "pcs_config(%s) for port %d and sds %d not yet fully implemented\n",
|
|
phy_modes(interface), link->port, link->sds);
|
|
|
|
mutex_lock(&ctrl->lock);
|
|
|
|
if (ctrl->cfg->setup_serdes) {
|
|
ret = ctrl->cfg->setup_serdes(ctrl, link->sds, interface);
|
|
if (ret < 0)
|
|
goto out;
|
|
}
|
|
|
|
if (ctrl->cfg->set_autoneg) {
|
|
ret = ctrl->cfg->set_autoneg(ctrl, link->sds, neg_mode);
|
|
if (ret < 0)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&ctrl->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct phylink_pcs *rtpcs_create(struct device *dev, struct device_node *np, int port);
|
|
struct phylink_pcs *rtpcs_create(struct device *dev, struct device_node *np, int port)
|
|
{
|
|
struct platform_device *pdev;
|
|
struct device_node *pcs_np;
|
|
struct rtpcs_ctrl *ctrl;
|
|
struct rtpcs_link *link;
|
|
int sds;
|
|
|
|
/*
|
|
* RTL838x devices have a built-in octa port RTL8218B PHY that is not attached via
|
|
* a SerDes. Allow to be called with an empty SerDes device node. In this case lookup
|
|
* the parent/driver node directly.
|
|
*/
|
|
if (np) {
|
|
if (!of_device_is_available(np))
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
if (of_property_read_u32(np, "reg", &sds))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
pcs_np = of_get_parent(np);
|
|
} else {
|
|
pcs_np = of_find_compatible_node(NULL, NULL, "realtek,otto-pcs");
|
|
sds = -1;
|
|
}
|
|
|
|
if (!pcs_np)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
if (!of_device_is_available(pcs_np)) {
|
|
of_node_put(pcs_np);
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
pdev = of_find_device_by_node(pcs_np);
|
|
of_node_put(pcs_np);
|
|
if (!pdev)
|
|
return ERR_PTR(-EPROBE_DEFER);
|
|
|
|
ctrl = platform_get_drvdata(pdev);
|
|
if (!ctrl) {
|
|
put_device(&pdev->dev);
|
|
return ERR_PTR(-EPROBE_DEFER);
|
|
}
|
|
|
|
if (port < 0 || port > ctrl->cfg->cpu_port)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
if (sds != -1 && rtpcs_sds_read(ctrl, sds, 0, 0) < 0)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
link = kzalloc(sizeof(*link), GFP_KERNEL);
|
|
if (!link) {
|
|
put_device(&pdev->dev);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
device_link_add(dev, ctrl->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
|
|
|
|
link->ctrl = ctrl;
|
|
link->port = port;
|
|
link->sds = sds;
|
|
link->pcs.ops = ctrl->cfg->pcs_ops;
|
|
link->pcs.neg_mode = true;
|
|
|
|
ctrl->link[port] = link;
|
|
|
|
dev_dbg(ctrl->dev, "phylink_pcs created, port %d, sds %d\n", port, sds);
|
|
|
|
return &link->pcs;
|
|
}
|
|
EXPORT_SYMBOL(rtpcs_create);
|
|
|
|
static struct mii_bus *rtpcs_probe_serdes_bus(struct rtpcs_ctrl *ctrl)
|
|
{
|
|
struct device_node *np;
|
|
struct mii_bus *bus;
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "realtek,otto-serdes-mdio");
|
|
if (!np) {
|
|
dev_err(ctrl->dev, "SerDes mdio bus not found in DT");
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
bus = of_mdio_find_bus(np);
|
|
of_node_put(np);
|
|
if (!bus) {
|
|
dev_warn(ctrl->dev, "SerDes mdio bus not (yet) active");
|
|
return ERR_PTR(-EPROBE_DEFER);
|
|
}
|
|
|
|
if (!of_device_is_available(np)) {
|
|
dev_err(ctrl->dev, "SerDes mdio bus not usable");
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
return bus;
|
|
}
|
|
|
|
static int rtpcs_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *child;
|
|
struct rtpcs_ctrl *ctrl;
|
|
u32 sds;
|
|
int ret;
|
|
|
|
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
|
if (!ctrl)
|
|
return -ENOMEM;
|
|
|
|
mutex_init(&ctrl->lock);
|
|
|
|
ctrl->dev = dev;
|
|
ctrl->cfg = (const struct rtpcs_config *)device_get_match_data(ctrl->dev);
|
|
ctrl->map = syscon_node_to_regmap(np->parent);
|
|
if (IS_ERR(ctrl->map))
|
|
return PTR_ERR(ctrl->map);
|
|
|
|
ctrl->bus = rtpcs_probe_serdes_bus(ctrl);
|
|
if (IS_ERR(ctrl->bus))
|
|
return PTR_ERR(ctrl->bus);
|
|
|
|
for_each_child_of_node(dev->of_node, child) {
|
|
ret = of_property_read_u32(child, "reg", &sds);
|
|
if (ret)
|
|
return ret;
|
|
if (sds >= RTPCS_SDS_CNT)
|
|
return -EINVAL;
|
|
|
|
ctrl->rx_pol_inv[sds] = of_property_read_bool(child, "realtek,pnswap-rx");
|
|
ctrl->tx_pol_inv[sds] = of_property_read_bool(child, "realtek,pnswap-tx");
|
|
}
|
|
|
|
/*
|
|
* rtpcs_create() relies on that fact that data is attached to the platform device to
|
|
* determine if the driver is ready. Do this after everything is initialized properly.
|
|
*/
|
|
platform_set_drvdata(pdev, ctrl);
|
|
|
|
dev_info(dev, "Realtek PCS driver initialized\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rtpcs_93xx_set_autoneg(struct rtpcs_ctrl *ctrl, int sds,
|
|
unsigned int neg_mode)
|
|
{
|
|
u16 bmcr = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? BMCR_ANENABLE : 0;
|
|
|
|
return rtpcs_sds_modify(ctrl, sds, 2, MII_BMCR, BMCR_ANENABLE, bmcr);
|
|
}
|
|
|
|
static const struct phylink_pcs_ops rtpcs_838x_pcs_ops = {
|
|
.pcs_an_restart = rtpcs_pcs_an_restart,
|
|
.pcs_config = rtpcs_pcs_config,
|
|
.pcs_get_state = rtpcs_pcs_get_state,
|
|
};
|
|
|
|
static const struct rtpcs_config rtpcs_838x_cfg = {
|
|
.cpu_port = RTPCS_838X_CPU_PORT,
|
|
.mac_link_dup_sts = RTPCS_838X_MAC_LINK_DUP_STS,
|
|
.mac_link_spd_sts = RTPCS_838X_MAC_LINK_SPD_STS,
|
|
.mac_link_spd_bits = RTPCS_83XX_MAC_LINK_SPD_BITS,
|
|
.mac_link_sts = RTPCS_838X_MAC_LINK_STS,
|
|
.mac_rx_pause_sts = RTPCS_838X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTPCS_838X_MAC_TX_PAUSE_STS,
|
|
.pcs_ops = &rtpcs_838x_pcs_ops,
|
|
};
|
|
|
|
static const struct phylink_pcs_ops rtpcs_839x_pcs_ops = {
|
|
.pcs_an_restart = rtpcs_pcs_an_restart,
|
|
.pcs_config = rtpcs_pcs_config,
|
|
.pcs_get_state = rtpcs_pcs_get_state,
|
|
};
|
|
|
|
static const struct rtpcs_config rtpcs_839x_cfg = {
|
|
.cpu_port = RTPCS_839X_CPU_PORT,
|
|
.mac_link_dup_sts = RTPCS_839X_MAC_LINK_DUP_STS,
|
|
.mac_link_spd_sts = RTPCS_839X_MAC_LINK_SPD_STS,
|
|
.mac_link_spd_bits = RTPCS_83XX_MAC_LINK_SPD_BITS,
|
|
.mac_link_sts = RTPCS_839X_MAC_LINK_STS,
|
|
.mac_rx_pause_sts = RTPCS_839X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTPCS_839X_MAC_TX_PAUSE_STS,
|
|
.pcs_ops = &rtpcs_839x_pcs_ops,
|
|
};
|
|
|
|
static const struct phylink_pcs_ops rtpcs_930x_pcs_ops = {
|
|
.pcs_an_restart = rtpcs_pcs_an_restart,
|
|
.pcs_config = rtpcs_pcs_config,
|
|
.pcs_get_state = rtpcs_pcs_get_state,
|
|
};
|
|
|
|
static const struct rtpcs_config rtpcs_930x_cfg = {
|
|
.cpu_port = RTPCS_930X_CPU_PORT,
|
|
.mac_link_dup_sts = RTPCS_930X_MAC_LINK_DUP_STS,
|
|
.mac_link_spd_sts = RTPCS_930X_MAC_LINK_SPD_STS,
|
|
.mac_link_spd_bits = RTPCS_93XX_MAC_LINK_SPD_BITS,
|
|
.mac_link_sts = RTPCS_930X_MAC_LINK_STS,
|
|
.mac_rx_pause_sts = RTPCS_930X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTPCS_930X_MAC_TX_PAUSE_STS,
|
|
.pcs_ops = &rtpcs_930x_pcs_ops,
|
|
.set_autoneg = rtpcs_93xx_set_autoneg,
|
|
.setup_serdes = rtpcs_930x_setup_serdes,
|
|
};
|
|
|
|
static const struct phylink_pcs_ops rtpcs_931x_pcs_ops = {
|
|
.pcs_an_restart = rtpcs_pcs_an_restart,
|
|
.pcs_config = rtpcs_pcs_config,
|
|
.pcs_get_state = rtpcs_pcs_get_state,
|
|
};
|
|
|
|
static const struct rtpcs_config rtpcs_931x_cfg = {
|
|
.cpu_port = RTPCS_931X_CPU_PORT,
|
|
.mac_link_dup_sts = RTPCS_931X_MAC_LINK_DUP_STS,
|
|
.mac_link_spd_sts = RTPCS_931X_MAC_LINK_SPD_STS,
|
|
.mac_link_spd_bits = RTPCS_93XX_MAC_LINK_SPD_BITS,
|
|
.mac_link_sts = RTPCS_931X_MAC_LINK_STS,
|
|
.mac_rx_pause_sts = RTPCS_931X_MAC_RX_PAUSE_STS,
|
|
.mac_tx_pause_sts = RTPCS_931X_MAC_TX_PAUSE_STS,
|
|
.pcs_ops = &rtpcs_931x_pcs_ops,
|
|
.set_autoneg = rtpcs_93xx_set_autoneg,
|
|
.setup_serdes = rtpcs_931x_setup_serdes,
|
|
};
|
|
|
|
static const struct of_device_id rtpcs_of_match[] = {
|
|
{
|
|
.compatible = "realtek,rtl8380-pcs",
|
|
.data = &rtpcs_838x_cfg,
|
|
},
|
|
{
|
|
.compatible = "realtek,rtl8392-pcs",
|
|
.data = &rtpcs_839x_cfg,
|
|
},
|
|
{
|
|
.compatible = "realtek,rtl9301-pcs",
|
|
.data = &rtpcs_930x_cfg,
|
|
},
|
|
{
|
|
.compatible = "realtek,rtl9311-pcs",
|
|
.data = &rtpcs_931x_cfg,
|
|
},
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, rtpcs_of_match);
|
|
|
|
static struct platform_driver rtpcs_driver = {
|
|
.driver = {
|
|
.name = "realtek-otto-pcs",
|
|
.of_match_table = rtpcs_of_match
|
|
},
|
|
.probe = rtpcs_probe,
|
|
};
|
|
module_platform_driver(rtpcs_driver);
|
|
|
|
MODULE_AUTHOR("Markus Stockhausen <markus.stockhausen@gmx.de>");
|
|
MODULE_DESCRIPTION("Realtek Otto SerDes PCS driver");
|
|
MODULE_LICENSE("GPL v2");
|