diff options
author | Yangbo Lu <yangbo.lu@nxp.com> | 2020-04-10 10:47:05 +0800 |
---|---|---|
committer | Petr Štetiar <ynezz@true.cz> | 2020-05-07 12:53:06 +0200 |
commit | cddd4591404fb4c53dc0b3c0b15b942cdbed4356 (patch) | |
tree | 392c1179de46b0f804e3789edca19069b64e6b44 /target/linux/layerscape/patches-5.4/701-net-0328-net-phy-Inphi-IN112525_s03-retimer-support.patch | |
parent | d1d2c0b5579ea4f69a42246c9318539d61ba1999 (diff) | |
download | upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.tar.gz upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.tar.bz2 upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.zip |
layerscape: add patches-5.4
Add patches for linux-5.4. The patches are from NXP LSDK-20.04 release
which was tagged LSDK-20.04-V5.4.
https://source.codeaurora.org/external/qoriq/qoriq-components/linux/
For boards LS1021A-IOT, and Traverse-LS1043 which are not involved in
LSDK, port the dts patches from 4.14.
The patches are sorted into the following categories:
301-arch-xxxx
302-dts-xxxx
303-core-xxxx
701-net-xxxx
801-audio-xxxx
802-can-xxxx
803-clock-xxxx
804-crypto-xxxx
805-display-xxxx
806-dma-xxxx
807-gpio-xxxx
808-i2c-xxxx
809-jailhouse-xxxx
810-keys-xxxx
811-kvm-xxxx
812-pcie-xxxx
813-pm-xxxx
814-qe-xxxx
815-sata-xxxx
816-sdhc-xxxx
817-spi-xxxx
818-thermal-xxxx
819-uart-xxxx
820-usb-xxxx
821-vfio-xxxx
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Diffstat (limited to 'target/linux/layerscape/patches-5.4/701-net-0328-net-phy-Inphi-IN112525_s03-retimer-support.patch')
-rw-r--r-- | target/linux/layerscape/patches-5.4/701-net-0328-net-phy-Inphi-IN112525_s03-retimer-support.patch | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-5.4/701-net-0328-net-phy-Inphi-IN112525_s03-retimer-support.patch b/target/linux/layerscape/patches-5.4/701-net-0328-net-phy-Inphi-IN112525_s03-retimer-support.patch new file mode 100644 index 0000000000..46f45dfc69 --- /dev/null +++ b/target/linux/layerscape/patches-5.4/701-net-0328-net-phy-Inphi-IN112525_s03-retimer-support.patch @@ -0,0 +1,620 @@ +From 630ee8f358d961a7c8295d60a112e27cbfe4478d Mon Sep 17 00:00:00 2001 +From: Florin Chiculita <florinlaurentiu.chiculita@nxp.com> +Date: Fri, 9 Nov 2018 06:20:36 +0200 +Subject: [PATCH] net/phy: Inphi IN112525_s03 retimer support + +Software controller for IN112525_s03 retimer + +Signed-off-by: Florin Chiculita <florinlaurentiu.chiculita@nxp.com> +--- + drivers/net/phy/Kconfig | 5 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/inphi.c | 578 +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 584 insertions(+) + create mode 100644 drivers/net/phy/inphi.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -478,6 +478,11 @@ config ICPLUS_PHY + ---help--- + Currently supports the IP175C and IP1001 PHYs. + ++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--- +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -88,6 +88,7 @@ obj-$(CONFIG_DP83848_PHY) += dp83848.o + obj-$(CONFIG_DP83867_PHY) += dp83867.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 +--- /dev/null ++++ b/drivers/net/phy/inphi.c +@@ -0,0 +1,578 @@ ++/* ++ * 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 mdio_wr(a, b) phy_write_mmd(inphi_phydev, MDIO_MMD_VEND1, (a), (b)) ++#define mdio_rd(a) phy_read_mmd(inphi_phydev, MDIO_MMD_VEND1, (a)) ++ ++#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; ++ ++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; ++ ++ /* Read device id from phy registers */ ++ id_lsb = phy_read_mmd(phydev, MDIO_MMD_VEND1, INPHI_S03_DEVICE_ID_MSB); ++ if (id_lsb < 0) ++ return -ENXIO; ++ ++ phy_id = id_lsb << 16; ++ ++ id_msb = phy_read_mmd(phydev, MDIO_MMD_VEND1, 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 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, ++}, ++}; ++ ++module_phy_driver(inphi_driver); ++ ++static struct mdio_device_id __maybe_unused inphi_tbl[] = { ++ { PHY_ID_IN112525, 0x0ff0fff0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(mdio, inphi_tbl); |