From 16c595a53c5f37c2345f4b4b82f7eb927c2dda5e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 24 Jan 2024 09:43:30 -0800 Subject: Revert "net: ethernet: qualcomm: Remove QDF24xx support" This reverts commit a2a7f98aeeec48118fac73c22bd54f8889815e16. Konrad mentioned that Qualcomm appears to use these devices, still, internally, even tho they never made it to the broader market. Link: https://lore.kernel.org/all/0679f568-60e7-47d8-b86e-052a9eb4c103@linaro.org/ Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qualcomm/emac/Makefile | 3 +- .../ethernet/qualcomm/emac/emac-sgmii-qdf2400.c | 215 +++++++++++++++++++++ .../ethernet/qualcomm/emac/emac-sgmii-qdf2432.c | 202 +++++++++++++++++++ drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 120 ++++++++++-- drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 2 + 5 files changed, 521 insertions(+), 21 deletions(-) create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c (limited to 'drivers/net/ethernet/qualcomm') diff --git a/drivers/net/ethernet/qualcomm/emac/Makefile b/drivers/net/ethernet/qualcomm/emac/Makefile index ffc00995acb9..61d15e091be2 100644 --- a/drivers/net/ethernet/qualcomm/emac/Makefile +++ b/drivers/net/ethernet/qualcomm/emac/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o emac-ethtool.o \ - emac-sgmii-fsm9900.o + emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o \ + emac-sgmii-qdf2400.o diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c new file mode 100644 index 000000000000..b29148ce7e05 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + */ + +/* Qualcomm Technologies, Inc. QDF2400 EMAC SGMII Controller driver. + */ + +#include +#include "emac.h" + +/* EMAC_SGMII register offsets */ +#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C +#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 +#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 +#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 +#define EMAC_SGMII_PHY_RESET_CTRL 0x00a8 +#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 + +/* SGMII digital lane registers */ +#define EMAC_SGMII_LN_DRVR_CTRL0 0x000C +#define EMAC_SGMII_LN_DRVR_CTRL1 0x0010 +#define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 +#define EMAC_SGMII_LN_TX_MARGINING 0x001C +#define EMAC_SGMII_LN_TX_PRE 0x0020 +#define EMAC_SGMII_LN_TX_POST 0x0024 +#define EMAC_SGMII_LN_TX_BAND_MODE 0x0060 +#define EMAC_SGMII_LN_LANE_MODE 0x0064 +#define EMAC_SGMII_LN_PARALLEL_RATE 0x007C +#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x00C0 +#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x00D8 +#define EMAC_SGMII_LN_VGA_INITVAL 0x013C +#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x0184 +#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x0190 +#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x019C +#define EMAC_SGMII_LN_RX_BAND 0x01A4 +#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x01C0 +#define EMAC_SGMII_LN_RSM_CONFIG 0x01F8 +#define EMAC_SGMII_LN_SIGDET_ENABLES 0x0230 +#define EMAC_SGMII_LN_SIGDET_CNTRL 0x0234 +#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x0238 +#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02AC +#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02B8 +#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02C8 +#define EMAC_SGMII_LN_RX_RESECODE_OFFSET 0x02CC + +/* SGMII digital lane register values */ +#define UCDR_STEP_BY_TWO_MODE0 BIT(7) +#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) +#define UCDR_ENABLE BIT(6) +#define UCDR_SO_SATURATION(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS4 BIT(7) +#define SIGDET_EN_PS0_TO_PS2 BIT(6) + +#define TXVAL_VALID_INIT BIT(4) +#define KR_PCIGEN3_MODE BIT(0) + +#define MAIN_EN BIT(0) + +#define TX_MARGINING_MUX BIT(6) +#define TX_MARGINING(x) ((x) & 0x3f) + +#define TX_PRE_MUX BIT(6) + +#define TX_POST_MUX BIT(6) + +#define CML_GEAR_MODE(x) (((x) & 7) << 3) +#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) + +#define RESCODE_OFFSET(x) ((x) & 0x1f) + +#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) +#define MIXER_DATARATE_MODE(x) ((x) & 3) + +#define VGA_THRESH_DFE(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) +#define SIGDET_FLT_BYP BIT(0) + +#define SIGDET_LVL(x) (((x) & 0xf) << 4) + +#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) + +#define INVERT_PCS_RX_CLK BIT(7) + +#define DRVR_LOGIC_CLK_EN BIT(4) +#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) + +#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) + +#define BAND_MODE0(x) ((x) & 0x3) + +#define LANE_MODE(x) ((x) & 0x1f) + +#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) +#define EN_DLL_MODE0 BIT(4) +#define EN_IQ_DCC_MODE0 BIT(3) +#define EN_IQCAL_MODE0 BIT(2) + +#define BYPASS_RSM_SAMP_CAL BIT(1) +#define BYPASS_RSM_DLL_CAL BIT(0) + +#define L0_RX_EQUALIZE_ENABLE BIT(6) + +#define PWRDN_B BIT(0) + +#define CDR_MAX_CNT(x) ((x) & 0xff) + +#define SERDES_START_WAIT_TIMES 100 + +struct emac_reg_write { + unsigned int offset; + u32 val; +}; + +static void emac_reg_write_all(void __iomem *base, + const struct emac_reg_write *itr, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++itr, ++i) + writel(itr->val, base + itr->offset); +} + +static const struct emac_reg_write sgmii_laned[] = { + /* CDR Settings */ + {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, + UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, + {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, + {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, + + /* TX/RX Settings */ + {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, + + {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, + {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, + {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, + {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, + {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, + + {EMAC_SGMII_LN_CML_CTRL_MODE0, + CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, + {EMAC_SGMII_LN_MIXER_CTRL_MODE0, + MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, + {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, + {EMAC_SGMII_LN_SIGDET_ENABLES, + SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, + {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, + + {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, + {EMAC_SGMII_LN_RX_MISC_CNTRL0, INVERT_PCS_RX_CLK}, + {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, + DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, + + {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, + {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(1)}, + {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(2)}, + {EMAC_SGMII_LN_DRVR_CTRL1, RESCODE_OFFSET(7)}, + {EMAC_SGMII_LN_RX_RESECODE_OFFSET, RESCODE_OFFSET(9)}, + {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, + {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(2) | + EN_DLL_MODE0 | EN_IQ_DCC_MODE0 | EN_IQCAL_MODE0}, + {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, +}; + +static const struct emac_reg_write physical_coding_sublayer_programming[] = { + {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, + {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, + {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, + {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, +}; + +int emac_sgmii_init_qdf2400(struct emac_adapter *adpt) +{ + struct emac_sgmii *phy = &adpt->phy; + void __iomem *phy_regs = phy->base; + void __iomem *laned = phy->digital; + unsigned int i; + u32 lnstatus; + + /* PCS lane-x init */ + emac_reg_write_all(phy->base, physical_coding_sublayer_programming, + ARRAY_SIZE(physical_coding_sublayer_programming)); + + /* SGMII lane-x init */ + emac_reg_write_all(phy->digital, sgmii_laned, ARRAY_SIZE(sgmii_laned)); + + /* Power up PCS and start reset lane state machine */ + + writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); + writel(1, laned + SGMII_LN_RSM_START); + + /* Wait for c_ready assertion */ + for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { + lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); + if (lnstatus & BIT(1)) + break; + usleep_range(100, 200); + } + + if (i == SERDES_START_WAIT_TIMES) { + netdev_err(adpt->netdev, "SGMII failed to start\n"); + return -EIO; + } + + /* Disable digital and SERDES loopback */ + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); + writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); + + /* Mask out all the SGMII Interrupt */ + writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c new file mode 100644 index 000000000000..65519eeebecd --- /dev/null +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + */ + +/* Qualcomm Technologies, Inc. QDF2432 EMAC SGMII Controller driver. + */ + +#include +#include "emac.h" + +/* EMAC_SGMII register offsets */ +#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C +#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 +#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 +#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 +#define EMAC_SGMII_PHY_RESET_CTRL 0x00a8 +#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 + +/* SGMII digital lane registers */ +#define EMAC_SGMII_LN_DRVR_CTRL0 0x000C +#define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 +#define EMAC_SGMII_LN_TX_MARGINING 0x001C +#define EMAC_SGMII_LN_TX_PRE 0x0020 +#define EMAC_SGMII_LN_TX_POST 0x0024 +#define EMAC_SGMII_LN_TX_BAND_MODE 0x0060 +#define EMAC_SGMII_LN_LANE_MODE 0x0064 +#define EMAC_SGMII_LN_PARALLEL_RATE 0x0078 +#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x00B8 +#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x00D0 +#define EMAC_SGMII_LN_VGA_INITVAL 0x0134 +#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x017C +#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x0188 +#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x0194 +#define EMAC_SGMII_LN_RX_BAND 0x019C +#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x01B8 +#define EMAC_SGMII_LN_RSM_CONFIG 0x01F0 +#define EMAC_SGMII_LN_SIGDET_ENABLES 0x0224 +#define EMAC_SGMII_LN_SIGDET_CNTRL 0x0228 +#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x022C +#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02A0 +#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02AC +#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02BC + +/* SGMII digital lane register values */ +#define UCDR_STEP_BY_TWO_MODE0 BIT(7) +#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) +#define UCDR_ENABLE BIT(6) +#define UCDR_SO_SATURATION(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS4 BIT(7) +#define SIGDET_EN_PS0_TO_PS2 BIT(6) + +#define TXVAL_VALID_INIT BIT(4) +#define KR_PCIGEN3_MODE BIT(0) + +#define MAIN_EN BIT(0) + +#define TX_MARGINING_MUX BIT(6) +#define TX_MARGINING(x) ((x) & 0x3f) + +#define TX_PRE_MUX BIT(6) + +#define TX_POST_MUX BIT(6) + +#define CML_GEAR_MODE(x) (((x) & 7) << 3) +#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) + +#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) +#define MIXER_DATARATE_MODE(x) ((x) & 3) + +#define VGA_THRESH_DFE(x) ((x) & 0x3f) + +#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) +#define SIGDET_FLT_BYP BIT(0) + +#define SIGDET_LVL(x) (((x) & 0xf) << 4) + +#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) + +#define DRVR_LOGIC_CLK_EN BIT(4) +#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) + +#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) + +#define BAND_MODE0(x) ((x) & 0x3) + +#define LANE_MODE(x) ((x) & 0x1f) + +#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) +#define BYPASS_RSM_SAMP_CAL BIT(1) +#define BYPASS_RSM_DLL_CAL BIT(0) + +#define L0_RX_EQUALIZE_ENABLE BIT(6) + +#define PWRDN_B BIT(0) + +#define CDR_MAX_CNT(x) ((x) & 0xff) + +#define SERDES_START_WAIT_TIMES 100 + +struct emac_reg_write { + unsigned int offset; + u32 val; +}; + +static void emac_reg_write_all(void __iomem *base, + const struct emac_reg_write *itr, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++itr, ++i) + writel(itr->val, base + itr->offset); +} + +static const struct emac_reg_write sgmii_laned[] = { + /* CDR Settings */ + {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, + UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, + {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, + {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, + + /* TX/RX Settings */ + {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, + + {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, + {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, + {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, + {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, + {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, + + {EMAC_SGMII_LN_CML_CTRL_MODE0, + CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, + {EMAC_SGMII_LN_MIXER_CTRL_MODE0, + MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, + {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, + {EMAC_SGMII_LN_SIGDET_ENABLES, + SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, + {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, + + {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, + {EMAC_SGMII_LN_RX_MISC_CNTRL0, 0}, + {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, + DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, + + {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, + {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)}, + {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)}, + {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, + {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)}, + {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, +}; + +static const struct emac_reg_write physical_coding_sublayer_programming[] = { + {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, + {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, + {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, + {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, +}; + +int emac_sgmii_init_qdf2432(struct emac_adapter *adpt) +{ + struct emac_sgmii *phy = &adpt->phy; + void __iomem *phy_regs = phy->base; + void __iomem *laned = phy->digital; + unsigned int i; + u32 lnstatus; + + /* PCS lane-x init */ + emac_reg_write_all(phy->base, physical_coding_sublayer_programming, + ARRAY_SIZE(physical_coding_sublayer_programming)); + + /* SGMII lane-x init */ + emac_reg_write_all(phy->digital, sgmii_laned, ARRAY_SIZE(sgmii_laned)); + + /* Power up PCS and start reset lane state machine */ + + writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); + writel(1, laned + SGMII_LN_RSM_START); + + /* Wait for c_ready assertion */ + for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { + lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); + if (lnstatus & BIT(1)) + break; + usleep_range(100, 200); + } + + if (i == SERDES_START_WAIT_TIMES) { + netdev_err(adpt->netdev, "SGMII failed to start\n"); + return -EIO; + } + + /* Disable digital and SERDES loopback */ + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); + writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); + writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); + + /* Mask out all the SGMII Interrupt */ + writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c index 573c82b21de5..e4bc18009d08 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -274,11 +275,76 @@ static struct sgmii_ops fsm9900_ops = { .reset = emac_sgmii_common_reset, }; +static struct sgmii_ops qdf2432_ops = { + .init = emac_sgmii_init_qdf2432, + .open = emac_sgmii_common_open, + .close = emac_sgmii_common_close, + .link_change = emac_sgmii_common_link_change, + .reset = emac_sgmii_common_reset, +}; + +#ifdef CONFIG_ACPI +static struct sgmii_ops qdf2400_ops = { + .init = emac_sgmii_init_qdf2400, + .open = emac_sgmii_common_open, + .close = emac_sgmii_common_close, + .link_change = emac_sgmii_common_link_change, + .reset = emac_sgmii_common_reset, +}; +#endif + +static int emac_sgmii_acpi_match(struct device *dev, void *data) +{ +#ifdef CONFIG_ACPI + static const struct acpi_device_id match_table[] = { + { + .id = "QCOM8071", + }, + {} + }; + const struct acpi_device_id *id = acpi_match_device(match_table, dev); + struct sgmii_ops **ops = data; + + if (id) { + acpi_handle handle = ACPI_HANDLE(dev); + unsigned long long hrv; + acpi_status status; + + status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); + if (status) { + if (status == AE_NOT_FOUND) + /* Older versions of the QDF2432 ACPI tables do + * not have an _HRV property. + */ + hrv = 1; + else + /* Something is wrong with the tables */ + return 0; + } + + switch (hrv) { + case 1: + *ops = &qdf2432_ops; + return 1; + case 2: + *ops = &qdf2400_ops; + return 1; + } + } +#endif + + return 0; +} + static const struct of_device_id emac_sgmii_dt_match[] = { { .compatible = "qcom,fsm9900-emac-sgmii", .data = &fsm9900_ops, }, + { + .compatible = "qcom,qdf2432-emac-sgmii", + .data = &qdf2432_ops, + }, {} }; @@ -289,30 +355,44 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) struct resource *res; int ret; - const struct of_device_id *match; - struct device_node *np; + if (has_acpi_companion(&pdev->dev)) { + struct device *dev; - np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); - if (!np) { - dev_err(&pdev->dev, "missing internal-phy property\n"); - return -ENODEV; - } + dev = device_find_child(&pdev->dev, &phy->sgmii_ops, + emac_sgmii_acpi_match); - sgmii_pdev = of_find_device_by_node(np); - of_node_put(np); - if (!sgmii_pdev) { - dev_err(&pdev->dev, "invalid internal-phy property\n"); - return -ENODEV; - } + if (!dev) { + dev_warn(&pdev->dev, "cannot find internal phy node\n"); + return 0; + } - match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); - if (!match) { - dev_err(&pdev->dev, "unrecognized internal phy node\n"); - ret = -ENODEV; - goto error_put_device; - } + sgmii_pdev = to_platform_device(dev); + } else { + const struct of_device_id *match; + struct device_node *np; - phy->sgmii_ops = (struct sgmii_ops *)match->data; + np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); + if (!np) { + dev_err(&pdev->dev, "missing internal-phy property\n"); + return -ENODEV; + } + + sgmii_pdev = of_find_device_by_node(np); + of_node_put(np); + if (!sgmii_pdev) { + dev_err(&pdev->dev, "invalid internal-phy property\n"); + return -ENODEV; + } + + match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); + if (!match) { + dev_err(&pdev->dev, "unrecognized internal phy node\n"); + ret = -ENODEV; + goto error_put_device; + } + + phy->sgmii_ops = (struct sgmii_ops *)match->data; + } /* Base address is the first address */ res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h index 7b7b0dc44225..6daeddacbcfa 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h @@ -40,6 +40,8 @@ struct emac_sgmii { int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt); int emac_sgmii_init_fsm9900(struct emac_adapter *adpt); +int emac_sgmii_init_qdf2432(struct emac_adapter *adpt); +int emac_sgmii_init_qdf2400(struct emac_adapter *adpt); int emac_sgmii_init(struct emac_adapter *adpt); int emac_sgmii_open(struct emac_adapter *adpt); -- cgit