diff options
author | Dong Aisheng <aisheng.dong@nxp.com> | 2021-11-30 14:59:46 +0800 |
---|---|---|
committer | Dong Aisheng <aisheng.dong@nxp.com> | 2021-11-30 14:59:46 +0800 |
commit | ff46675e60bf540895fcc9aac1160a40ed89509f (patch) | |
tree | 6ff278c77063d4ee69e91b5992436dac8ccf05c9 /drivers/net | |
parent | e700345aa611be2d648bde4225d3105d5d6e45a4 (diff) | |
parent | 4f40d04cc896ae1549e0a53f962ff7b8b535e57f (diff) |
Merge remote-tracking branch 'origin/net/phy' into net/next
* origin/net/phy: (11 commits)
MLK-25746 net: phy: tja11xx: fix -Wimplicit-fallthrough build warning
net: phy: inphi: add a remove function
MLK-24174-03 net: phy: tja11xx: add refclk source selection support
LF-538 net: phy: tja11xx: add user interface to enable slave mode
drivers: net: phy: aquantia: enable USX AN for USXGMII protocol
...
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/phy/Kconfig | 5 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/aquantia_main.c | 101 | ||||
-rw-r--r-- | drivers/net/phy/inphi.c | 601 | ||||
-rw-r--r-- | drivers/net/phy/nxp-tja11xx.c | 166 | ||||
-rw-r--r-- | drivers/net/phy/swphy.c | 1 |
6 files changed, 870 insertions, 5 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 902495afcb38..fae7ddb545e8 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -178,6 +178,11 @@ config LXT_PHY help Currently supports the lxt970, lxt971 +config INPHI_PHY + tristate "Inphi CDR 10G/25G Ethernet PHY" + help + Currently supports the IN112525_S03 part @ 25G + config INTEL_XWAY_PHY tristate "Intel XWAY PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index b2728d00fc9a..57c557873d38 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_DP83869_PHY) += dp83869.o obj-$(CONFIG_DP83TC811_PHY) += dp83tc811.o obj-$(CONFIG_FIXED_PHY) += fixed_phy.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o +obj-$(CONFIG_INPHI_PHY) += inphi.o obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o obj-$(CONFIG_LXT_PHY) += lxt.o diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 968dd43a2b1e..c0976adbe0df 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -22,6 +22,8 @@ #define PHY_ID_AQR107 0x03a1b4e0 #define PHY_ID_AQCS109 0x03a1b5c2 #define PHY_ID_AQR405 0x03a1b4b0 +#define PHY_ID_AQR112 0x03a1b662 +#define PHY_ID_AQR412 0x03a1b712 #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) @@ -31,6 +33,9 @@ #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10 +#define MDIO_PHYXS_VEND_PROV2 0xC441 +#define MDIO_PHYXS_VEND_PROV2_USX_AN BIT(3) + #define MDIO_AN_VEND_PROV 0xc400 #define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) #define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) @@ -122,6 +127,29 @@ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) +/* registers in MDIO_MMD_VEND1 region */ +#define AQUANTIA_VND1_GLOBAL_SC 0x000 +#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb) + +/* global start rate, the protocol associated with this speed is used by default + * on SI. + */ +#define AQUANTIA_VND1_GSTART_RATE 0x31a +#define AQUANTIA_VND1_GSTART_RATE_OFF 0 +#define AQUANTIA_VND1_GSTART_RATE_100M 1 +#define AQUANTIA_VND1_GSTART_RATE_1G 2 +#define AQUANTIA_VND1_GSTART_RATE_10G 3 +#define AQUANTIA_VND1_GSTART_RATE_2_5G 4 +#define AQUANTIA_VND1_GSTART_RATE_5G 5 + +/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */ +#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b +#define AQUANTIA_VND1_GSYSCFG_100M 0 +#define AQUANTIA_VND1_GSYSCFG_1G 1 +#define AQUANTIA_VND1_GSYSCFG_2_5G 2 +#define AQUANTIA_VND1_GSYSCFG_5G 3 +#define AQUANTIA_VND1_GSYSCFG_10G 4 + struct aqr107_hw_stat { const char *name; int reg; @@ -242,6 +270,61 @@ static int aqr_config_aneg(struct phy_device *phydev) return genphy_c45_check_and_restart_aneg(phydev, changed); } +static struct { + u16 syscfg; + int cnt; + u16 start_rate; +} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = { + [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G, + AQUANTIA_VND1_GSTART_RATE_1G}, + [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G, + AQUANTIA_VND1_GSTART_RATE_2_5G}, + [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G, + AQUANTIA_VND1_GSTART_RATE_10G}, + [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G, + AQUANTIA_VND1_GSTART_RATE_10G}, +}; + +/* Sets up protocol on system side before calling aqr_config_aneg */ +static int aqr_config_aneg_set_prot(struct phy_device *phydev) +{ + int if_type = phydev->interface; + int i; + + if (!aquantia_syscfg[if_type].cnt) + return 0; + + /* set PHY in low power mode so we can configure protocols */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, + AQUANTIA_VND1_GLOBAL_SC_LP); + mdelay(10); + + /* set the default rate to enable the SI link */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, + aquantia_syscfg[if_type].start_rate); + + for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) { + u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, + AQUANTIA_VND1_GSYSCFG_BASE + i); + if (!reg) + continue; + + phy_write_mmd(phydev, MDIO_MMD_VEND1, + AQUANTIA_VND1_GSYSCFG_BASE + i, + aquantia_syscfg[if_type].syscfg); + } + + if (if_type == PHY_INTERFACE_MODE_USXGMII) + phy_write_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_PROV2, + MDIO_PHYXS_VEND_PROV2_USX_AN); + + /* wake PHY back up */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); + mdelay(10); + + return aqr_config_aneg(phydev); +} + static int aqr_config_intr(struct phy_device *phydev) { bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; @@ -686,6 +769,22 @@ static struct phy_driver aqr_driver[] = { .handle_interrupt = aqr_handle_interrupt, .read_status = aqr_read_status, }, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR112), + .name = "Aquantia AQR112", + .config_aneg = aqr_config_aneg_set_prot, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr_read_status, +}, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR412), + .name = "Aquantia AQR412", + .config_aneg = aqr_config_aneg_set_prot, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr_read_status, +}, }; module_phy_driver(aqr_driver); @@ -698,6 +797,8 @@ static struct mdio_device_id __maybe_unused aqr_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, { } }; diff --git a/drivers/net/phy/inphi.c b/drivers/net/phy/inphi.c new file mode 100644 index 000000000000..14762d35bc15 --- /dev/null +++ b/drivers/net/phy/inphi.c @@ -0,0 +1,601 @@ +/* + * Copyright 2018 NXP + * Copyright 2018 INPHI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Inphi is a registered trademark of Inphi Corporation + * + */ + +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/mdio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_irq.h> +#include <linux/workqueue.h> +#include <linux/i2c.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#define PHY_ID_IN112525 0x02107440 + +#define INPHI_S03_DEVICE_ID_MSB 0x2 +#define INPHI_S03_DEVICE_ID_LSB 0x3 + +#define ALL_LANES 4 +#define INPHI_POLL_DELAY 2500 + +#define PHYCTRL_REG1 0x0012 +#define PHYCTRL_REG2 0x0014 +#define PHYCTRL_REG3 0x0120 +#define PHYCTRL_REG4 0x0121 +#define PHYCTRL_REG5 0x0180 +#define PHYCTRL_REG6 0x0580 +#define PHYCTRL_REG7 0x05C4 +#define PHYCTRL_REG8 0x01C8 +#define PHYCTRL_REG9 0x0521 + +#define PHYSTAT_REG1 0x0021 +#define PHYSTAT_REG2 0x0022 +#define PHYSTAT_REG3 0x0123 + +#define PHYMISC_REG1 0x0025 +#define PHYMISC_REG2 0x002c +#define PHYMISC_REG3 0x00b3 +#define PHYMISC_REG4 0x0181 +#define PHYMISC_REG5 0x019D +#define PHYMISC_REG6 0x0198 +#define PHYMISC_REG7 0x0199 +#define PHYMISC_REG8 0x0581 +#define PHYMISC_REG9 0x0598 +#define PHYMISC_REG10 0x059c +#define PHYMISC_REG20 0x01B0 +#define PHYMISC_REG21 0x01BC +#define PHYMISC_REG22 0x01C0 + +#define RX_VCO_CODE_OFFSET 5 +#define VCO_CODE 390 + +int vco_codes[ALL_LANES] = { + VCO_CODE, + VCO_CODE, + VCO_CODE, + VCO_CODE +}; + +static void mykmod_work_handler(struct work_struct *w); + +static struct workqueue_struct *wq; +static DECLARE_DELAYED_WORK(mykmod_work, mykmod_work_handler); +static unsigned long onesec; +struct phy_device *inphi_phydev; + +static int mdio_wr(u32 regnum, u16 val) +{ + regnum = MII_ADDR_C45 | (MDIO_MMD_VEND1 << 16) | (regnum & 0xffff); + + return mdiobus_write(inphi_phydev->mdio.bus, inphi_phydev->mdio.addr, + regnum, val); +} + +static int mdio_rd(u32 regnum) +{ + regnum = MII_ADDR_C45 | (MDIO_MMD_VEND1 << 16) | (regnum & 0xffff); + + return mdiobus_read(inphi_phydev->mdio.bus, inphi_phydev->mdio.addr, + regnum); +} + + +int bit_test(int value, int bit_field) +{ + int result; + int bit_mask = (1 << bit_field); + + result = ((value & bit_mask) == bit_mask); + return result; +} + +int tx_pll_lock_test(int lane) +{ + int i, val, locked = 1; + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) { + val = mdio_rd(i * 0x100 + PHYSTAT_REG3); + locked = locked & bit_test(val, 15); + } + } else { + val = mdio_rd(lane * 0x100 + PHYSTAT_REG3); + locked = locked & bit_test(val, 15); + } + + return locked; +} + +void rx_reset_assert(int lane) +{ + int mask, val; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + mask = (1 << 15); + mdio_wr(PHYMISC_REG2, val + mask); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = (1 << 6); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask); + } +} + +void rx_reset_de_assert(int lane) +{ + int mask, val; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + mask = 0xffff - (1 << 15); + mdio_wr(PHYMISC_REG2, val & mask); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = 0xffff - (1 << 6); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask); + } +} + +void rx_powerdown_assert(int lane) +{ + int mask, val; + + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = (1 << 5); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask); +} + +void rx_powerdown_de_assert(int lane) +{ + int mask, val; + + val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); + mask = 0xffff - (1 << 5); + mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask); +} + +void tx_pll_assert(int lane) +{ + int val, recal; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + recal = (1 << 12); + mdio_wr(PHYMISC_REG2, val | recal); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG4); + recal = (1 << 15); + mdio_wr(lane * 0x100 + PHYCTRL_REG4, val | recal); + } +} + +void tx_pll_de_assert(int lane) +{ + int recal, val; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + recal = 0xefff; + mdio_wr(PHYMISC_REG2, val & recal); + } else { + val = mdio_rd(lane * 0x100 + PHYCTRL_REG4); + recal = 0x7fff; + mdio_wr(lane * 0x100 + PHYCTRL_REG4, val & recal); + } +} + +void tx_core_assert(int lane) +{ + int recal, val, val2, core_reset; + + if (lane == 4) { + val = mdio_rd(PHYMISC_REG2); + recal = 1 << 10; + mdio_wr(PHYMISC_REG2, val | recal); + } else { + val2 = mdio_rd(PHYMISC_REG3); + core_reset = (1 << (lane + 8)); + mdio_wr(PHYMISC_REG3, val2 | core_reset); + } +} + +void lol_disable(int lane) +{ + int val, mask; + + val = mdio_rd(PHYMISC_REG3); + mask = 1 << (lane + 4); + mdio_wr(PHYMISC_REG3, val | mask); +} + +void tx_core_de_assert(int lane) +{ + int val, recal, val2, core_reset; + + if (lane == ALL_LANES) { + val = mdio_rd(PHYMISC_REG2); + recal = 0xffff - (1 << 10); + mdio_wr(PHYMISC_REG2, val & recal); + } else { + val2 = mdio_rd(PHYMISC_REG3); + core_reset = 0xffff - (1 << (lane + 8)); + mdio_wr(PHYMISC_REG3, val2 & core_reset); + } +} + +void tx_restart(int lane) +{ + tx_core_assert(lane); + tx_pll_assert(lane); + tx_pll_de_assert(lane); + usleep_range(1500, 1600); + tx_core_de_assert(lane); +} + +void disable_lane(int lane) +{ + rx_reset_assert(lane); + rx_powerdown_assert(lane); + tx_core_assert(lane); + lol_disable(lane); +} + +void toggle_reset(int lane) +{ + int reg, val, orig; + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG2, 0x8000); + udelay(100); + mdio_wr(PHYMISC_REG2, 0x0000); + } else { + reg = lane * 0x100 + PHYCTRL_REG8; + val = (1 << 6); + orig = mdio_rd(reg); + mdio_wr(reg, orig + val); + udelay(100); + mdio_wr(reg, orig); + } +} + +int az_complete_test(int lane) +{ + int success = 1, value; + + if (lane == 0 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5); + success = success & bit_test(value, 2); + } + if (lane == 1 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5 + 0x100); + success = success & bit_test(value, 2); + } + if (lane == 2 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5 + 0x200); + success = success & bit_test(value, 2); + } + if (lane == 3 || lane == ALL_LANES) { + value = mdio_rd(PHYCTRL_REG5 + 0x300); + success = success & bit_test(value, 2); + } + + return success; +} + +void save_az_offsets(int lane) +{ + int i; + +#define AZ_OFFSET_LANE_UPDATE(reg, lane) \ + mdio_wr((reg) + (lane) * 0x100, \ + (mdio_rd((reg) + (lane) * 0x100) >> 8)) + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) { + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, i); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, i); + } + } else { + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, lane); + AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, lane); + } + + mdio_wr(PHYCTRL_REG7, 0x0001); +} + +void save_vco_codes(int lane) +{ + int i; + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) { + vco_codes[i] = mdio_rd(PHYMISC_REG5 + i * 0x100); + mdio_wr(PHYMISC_REG5 + i * 0x100, + vco_codes[i] + RX_VCO_CODE_OFFSET); + } + } else { + vco_codes[lane] = mdio_rd(PHYMISC_REG5 + lane * 0x100); + mdio_wr(PHYMISC_REG5 + lane * 0x100, + vco_codes[lane] + RX_VCO_CODE_OFFSET); + } +} + +int inphi_lane_recovery(int lane) +{ + int i, value, az_pass; + + switch (lane) { + case 0: + case 1: + case 2: + case 3: + rx_reset_assert(lane); + mdelay(20); + break; + case ALL_LANES: + mdio_wr(PHYMISC_REG2, 0x9C00); + mdelay(20); + do { + value = mdio_rd(PHYMISC_REG2); + udelay(10); + } while (!bit_test(value, 4)); + break; + default: + dev_err(&inphi_phydev->mdio.dev, + "Incorrect usage of APIs in %s driver\n", + inphi_phydev->drv->name); + break; + } + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) + mdio_wr(PHYMISC_REG7 + i * 0x100, VCO_CODE); + } else { + mdio_wr(PHYMISC_REG7 + lane * 0x100, VCO_CODE); + } + + if (lane == ALL_LANES) + for (i = 0; i < ALL_LANES; i++) + mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0418); + else + mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0418); + + mdio_wr(PHYCTRL_REG7, 0x0000); + + rx_reset_de_assert(lane); + + if (lane == ALL_LANES) { + for (i = 0; i < ALL_LANES; i++) { + mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0410); + mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0412); + } + } else { + mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0410); + mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0412); + } + + for (i = 0; i < 64; i++) { + mdelay(100); + az_pass = az_complete_test(lane); + if (az_pass) { + save_az_offsets(lane); + break; + } + } + + if (!az_pass) { + pr_info("in112525: AZ calibration fail @ lane=%d\n", lane); + return -1; + } + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG8, 0x0002); + mdio_wr(PHYMISC_REG9, 0x2028); + mdio_wr(PHYCTRL_REG6, 0x0010); + usleep_range(1000, 1200); + mdio_wr(PHYCTRL_REG6, 0x0110); + mdelay(30); + mdio_wr(PHYMISC_REG9, 0x3020); + } else { + mdio_wr(PHYMISC_REG4 + lane * 0x100, 0x0002); + mdio_wr(PHYMISC_REG6 + lane * 0x100, 0x2028); + mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0010); + usleep_range(1000, 1200); + mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0110); + mdelay(30); + mdio_wr(PHYMISC_REG6 + lane * 0x100, 0x3020); + } + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG2, 0x1C00); + mdio_wr(PHYMISC_REG2, 0x0C00); + } else { + tx_restart(lane); + mdelay(11); + } + + if (lane == ALL_LANES) { + if (bit_test(mdio_rd(PHYMISC_REG2), 6) == 0) + return -1; + } else { + if (tx_pll_lock_test(lane) == 0) + return -1; + } + + save_vco_codes(lane); + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG2, 0x0400); + mdio_wr(PHYMISC_REG2, 0x0000); + value = mdio_rd(PHYCTRL_REG1); + value = value & 0xffbf; + mdio_wr(PHYCTRL_REG2, value); + } else { + tx_core_de_assert(lane); + } + + if (lane == ALL_LANES) { + mdio_wr(PHYMISC_REG1, 0x8000); + mdio_wr(PHYMISC_REG1, 0x0000); + } + mdio_rd(PHYMISC_REG1); + mdio_rd(PHYMISC_REG1); + usleep_range(1000, 1200); + mdio_rd(PHYSTAT_REG1); + mdio_rd(PHYSTAT_REG2); + + return 0; +} + +static void mykmod_work_handler(struct work_struct *w) +{ + int all_lanes_lock, lane0_lock, lane1_lock, lane2_lock, lane3_lock; + + lane0_lock = bit_test(mdio_rd(0x123), 15); + lane1_lock = bit_test(mdio_rd(0x223), 15); + lane2_lock = bit_test(mdio_rd(0x323), 15); + lane3_lock = bit_test(mdio_rd(0x423), 15); + + /* check if the chip had any successful lane lock from the previous + * stage (e.g. u-boot) + */ + all_lanes_lock = lane0_lock | lane1_lock | lane2_lock | lane3_lock; + + if (!all_lanes_lock) { + /* start fresh */ + inphi_lane_recovery(ALL_LANES); + } else { + if (!lane0_lock) + inphi_lane_recovery(0); + if (!lane1_lock) + inphi_lane_recovery(1); + if (!lane2_lock) + inphi_lane_recovery(2); + if (!lane3_lock) + inphi_lane_recovery(3); + } + + queue_delayed_work(wq, &mykmod_work, onesec); +} + +int inphi_probe(struct phy_device *phydev) +{ + int phy_id = 0, id_lsb = 0, id_msb = 0; + + /* setup the inphi_phydev ptr for mdio_rd/mdio_wr APIs */ + inphi_phydev = phydev; + + /* Read device id from phy registers */ + id_lsb = mdio_rd(INPHI_S03_DEVICE_ID_MSB); + if (id_lsb < 0) + return -ENXIO; + + phy_id = id_lsb << 16; + + id_msb = mdio_rd(INPHI_S03_DEVICE_ID_LSB); + if (id_msb < 0) + return -ENXIO; + + phy_id |= id_msb; + + /* Make sure the device tree binding matched the driver with the + * right device. + */ + if (phy_id != phydev->drv->phy_id) { + dev_err(&phydev->mdio.dev, + "Error matching phy with %s driver\n", + phydev->drv->name); + return -ENODEV; + } + + /* update the local phydev pointer, used inside all APIs */ + inphi_phydev = phydev; + onesec = msecs_to_jiffies(INPHI_POLL_DELAY); + + wq = create_singlethread_workqueue("inphi_kmod"); + if (wq) { + queue_delayed_work(wq, &mykmod_work, onesec); + } else { + dev_err(&phydev->mdio.dev, + "Error creating kernel workqueue for %s driver\n", + phydev->drv->name); + return -ENOMEM; + } + + return 0; +} + +static void inphi_remove(struct phy_device *phydev) +{ + cancel_delayed_work_sync(&mykmod_work); + destroy_workqueue(wq); +} + +static struct phy_driver inphi_driver[] = { +{ + .phy_id = PHY_ID_IN112525, + .phy_id_mask = 0x0ff0fff0, + .name = "Inphi 112525_S03", + .features = PHY_GBIT_FEATURES, + .probe = &inphi_probe, + .remove = &inphi_remove, +}, +}; + +module_phy_driver(inphi_driver); + +static struct mdio_device_id __maybe_unused inphi_tbl[] = { + { PHY_ID_IN112525, 0x0ff0fff0}, + {}, +}; + +MODULE_DEVICE_TABLE(mdio, inphi_tbl); diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c index 9944cc501806..6fb526c86147 100644 --- a/drivers/net/phy/nxp-tja11xx.c +++ b/drivers/net/phy/nxp-tja11xx.c @@ -10,6 +10,7 @@ #include <linux/mdio.h> #include <linux/mii.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/phy.h> #include <linux/hwmon.h> #include <linux/bitfield.h> @@ -34,10 +35,13 @@ #define MII_CFG1 18 #define MII_CFG1_MASTER_SLAVE BIT(15) #define MII_CFG1_AUTO_OP BIT(14) +#define MII_CFG1_MII_MODE GENMASK(9, 8) #define MII_CFG1_SLEEP_CONFIRM BIT(6) #define MII_CFG1_LED_MODE_MASK GENMASK(5, 4) #define MII_CFG1_LED_MODE_LINKUP 0 #define MII_CFG1_LED_ENABLE BIT(3) +#define MII_CFG1_MODE_REFCLK_IN 0x100 +#define MII_CFG1_MODE_REFCLK_OUT 0x200 #define MII_CFG2 19 #define MII_CFG2_SLEEP_REQUEST_TO GENMASK(1, 0) @@ -72,11 +76,14 @@ #define MII_COMMCFG 27 #define MII_COMMCFG_AUTO_OP BIT(15) +#define TJA110X_REFCLK_IN (0x1 << 0) + struct tja11xx_priv { char *hwmon_name; struct device *hwmon_dev; struct phy_device *phydev; struct work_struct phy_register_work; + u32 quirks; }; struct tja11xx_phy_stats { @@ -253,6 +260,8 @@ do_test: static int tja11xx_config_init(struct phy_device *phydev) { + struct tja11xx_priv *priv = phydev->priv; + int reg_mask, reg_val = 0; int ret; ret = tja11xx_enable_reg_write(phydev); @@ -265,15 +274,35 @@ static int tja11xx_config_init(struct phy_device *phydev) switch (phydev->phy_id & PHY_ID_MASK) { case PHY_ID_TJA1100: - ret = phy_modify(phydev, MII_CFG1, - MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK | - MII_CFG1_LED_ENABLE, - MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_LINKUP | - MII_CFG1_LED_ENABLE); + reg_mask = MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK | + MII_CFG1_LED_ENABLE; + reg_val = MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_LINKUP | + MII_CFG1_LED_ENABLE; + + reg_mask |= MII_CFG1_MII_MODE; + if (phydev->interface == PHY_INTERFACE_MODE_RMII) { + if (priv->quirks & TJA110X_REFCLK_IN) + reg_val |= MII_CFG1_MODE_REFCLK_IN; + else + reg_val |= MII_CFG1_MODE_REFCLK_OUT; + } + + ret = phy_modify(phydev, MII_CFG1, reg_mask, reg_val); if (ret) return ret; break; case PHY_ID_TJA1101: + reg_mask = MII_CFG1_MII_MODE; + if (phydev->interface == PHY_INTERFACE_MODE_RMII) { + if (priv->quirks & TJA110X_REFCLK_IN) + reg_val = MII_CFG1_MODE_REFCLK_IN; + else + reg_val = MII_CFG1_MODE_REFCLK_OUT; + } + ret = phy_modify(phydev, MII_CFG1, reg_mask, reg_val); + if (ret) + return ret; + fallthrough; case PHY_ID_TJA1102: ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP); if (ret) @@ -440,11 +469,128 @@ static const struct hwmon_chip_info tja11xx_hwmon_chip_info = { .info = tja11xx_hwmon_info, }; +/* Helper function, configures phy as master or slave + * @param phydev the phy to be configured + * @param setmaster ==0: set to slave + * !=0: set to master + * @return 0 on success, error code on failure + */ +static int set_master_cfg(struct phy_device *phydev, int setmaster) +{ + int err; + + /* disable link control prior to master/slave cfg */ + tja11xx_disable_link_control(phydev); + + err = phy_modify(phydev, MII_CFG1, MII_CFG1_MASTER_SLAVE, + setmaster ? MII_CFG1_MASTER_SLAVE : 0); + if (err < 0) + goto phy_configure_error; + + /* enable link control after master/slave cfg was set */ + tja11xx_enable_link_control(phydev); + + return 0; + +/* error handling */ +phy_configure_error: + dev_err(&phydev->mdio.dev, "phy r/w error\n"); + return err; +} + +/* Helper function, reads master/slave configuration of phy + * @param phydev the phy to be read + * + * @return ==0: is slave + * !=0: is master + */ +static int get_master_cfg(struct phy_device *phydev) +{ + int reg_val; + + /* read the current configuration */ + reg_val = phy_read(phydev, MII_CFG1); + if (reg_val < 0) + goto phy_read_error; + + return reg_val & MII_CFG1_MASTER_SLAVE; + +/* error handling */ +phy_read_error: + dev_err(&phydev->mdio.dev, "read error\n"); + return reg_val; +} + +/* This function handles read accesses to the node 'master_cfg' in + * sysfs. + * Depending on current configuration of the phy, the node reads + * 'master' or 'slave' + */ +static ssize_t master_cfg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int is_master; + struct phy_device *phydev = to_phy_device(dev); + + is_master = get_master_cfg(phydev); + + /* write result into the buffer */ + return scnprintf(buf, PAGE_SIZE, "%s\n", + is_master ? "master" : "slave"); +} + +/* This function handles write accesses to the node 'master_cfg' in sysfs. + * Depending on the value written to it, the phy is configured as + * master or slave + */ +static ssize_t master_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int err; + int setmaster; + struct phy_device *phydev = to_phy_device(dev); + + /* parse the buffer */ + err = kstrtoint(buf, 10, &setmaster); + if (err < 0) + goto phy_parse_error; + + /* write configuration to the phy */ + err = set_master_cfg(phydev, setmaster); + if (err < 0) + goto phy_cfg_error; + + return count; + +/* error handling */ +phy_parse_error: + dev_err(&phydev->mdio.dev, "parse failed\n"); + return err; + +phy_cfg_error: + dev_err(&phydev->mdio.dev, "phy cfg error\n"); + return err; +} + +static DEVICE_ATTR_RW(master_cfg); + +static struct attribute *nxp_sysfs_entries[] = { + &dev_attr_master_cfg.attr, + NULL +}; + +static struct attribute_group nxp_attribute_group = { + .name = "configuration", + .attrs = nxp_sysfs_entries, +}; + static int tja11xx_hwmon_register(struct phy_device *phydev, struct tja11xx_priv *priv) { struct device *dev = &phydev->mdio.dev; int i; + int ret; priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); if (!priv->hwmon_name) @@ -454,6 +600,16 @@ static int tja11xx_hwmon_register(struct phy_device *phydev, if (hwmon_is_bad_char(priv->hwmon_name[i])) priv->hwmon_name[i] = '_'; + if (dev->of_node && + of_property_read_bool(dev->of_node, "tja110x,refclk_in")) + priv->quirks |= TJA110X_REFCLK_IN; + + /* register sysfs files */ + phydev->priv = priv; + ret = sysfs_create_group(&phydev->mdio.dev.kobj, &nxp_attribute_group); + if (ret) + return ret; + priv->hwmon_dev = devm_hwmon_device_register_with_info(dev, priv->hwmon_name, phydev, diff --git a/drivers/net/phy/swphy.c b/drivers/net/phy/swphy.c index 59f1ba4d49bc..522ecda43d1b 100644 --- a/drivers/net/phy/swphy.c +++ b/drivers/net/phy/swphy.c @@ -71,6 +71,7 @@ static const struct swmii_regs duplex[] = { static int swphy_decode_speed(int speed) { switch (speed) { + case 10000: case 1000: return SWMII_SPEED_1000; case 100: |