openwrt-6.x/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c
Sven Eckelmann 29a9c3feb1 realtek: Fix alignment of parameters
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>
2025-11-25 00:28:51 +01:00

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");