// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2026 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "phy-aml-hsgmii.h" #define HSG_UPCTX_SEL_PDR_POST BIT(19) #define HSG_UPCTX_BOOST_EN BIT(22) #define HSG_UPCTX_BOOST_CTRL_MASK GENMASK(4, 0) #define HSG_UPCTX_BOOST_CTRL_SHIFT 0 #define HSG_UPCTX_PUIO_MAIN_MASK GENMASK(7, 5) #define HSG_UPCTX_PUIO_MAIN_SHIFT 5 #define HSG_UPCTX_PDIO_MAIN_MASK GENMASK(10, 8) #define HSG_UPCTX_PDIO_MAIN_SHIFT 8 #define HSG_DCHA_AFE_UPCRX_MASK GENMASK(7, 4) #define HSG_DCHA_AFE_UPCRX_SHIFT 4 #define HSG_RESET_TIME_US 100 /* Only for PHY Sync Link mode */ #define HSG_WAIT_SPEED_DETECT_TIMER 0xA2C3 #define HSG_PHY_DEBUG #ifdef HSG_PHY_DEBUG #define hsg_debug(fmt, args...) pr_info(fmt, ##args) #else #define hsg_debug(fmt, args...) pr_debug(fmt, ##args) #endif struct amlogic_hsgmii_phy_data { struct regmap *regmap; struct clk *hsg_general_clk; /* Warning: Must not disable the clk */ struct reset_control *hsg_reset; /* The Logic reset of HSGMII PHY */ struct reset_control *hsg_apb_reset; /* The Reg reset of HSGMII PHY */ void __iomem *hsg_trim_reg; /* HSGMII trim reg base */ void __iomem *u3hsg_combo_reg; /* U3 HSGMII combo reg base */ u32 combo_hsg_sel_val; /* Value to select HSGMII mode */ bool power_on; /* Current speed of HSGMII PHY */ int speed; /* True: PHY Sync Link, False: Fast Link */ bool psl_mode; /* Only for Fast Link mode. If set, phy speed change will lead to Rx fsm restart */ bool rx_fsm_restart; /* Only for Fast Link mode. If set, phy speed change will lead to pll fsm restart */ bool pll_fsm_restart; #ifdef HSG_PHY_DEBUG struct hsg_phy_stats hsg_stats; #endif }; static int aml_regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { return regmap_update_bits_base(map, reg, mask, val, NULL, false, true); } static void amlogic_hsgmii_phy_init_1g(struct regmap *regmap) { /* TODO */ } static void amlogic_hsgmii_phy_init_2p5g(struct regmap *regmap) { /* TODO */ } static inline int amlogic_hsgmii_phy_poll_status(struct regmap *regmap, unsigned int reg, unsigned int bit) { unsigned int val; return regmap_read_poll_timeout(regmap, reg, val, val & bit, 1500, 750000); } static inline int amlogic_hsgmii_phy_reset(struct phy *phy) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); struct device *dev = phy->dev.parent; int ret; /* Reset hsgmii initial fsm and flow */ ret = reset_control_reset(data->hsg_reset); if (ret) { dev_err(dev, "failed to reset HSGMII PHY: %d\n", ret); return ret; } // aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, // HSG_INIT_CTRL_RESET, // HSG_INIT_CTRL_RESET); udelay(HSG_RESET_TIME_US); // aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, // HSG_INIT_CTRL_RESET, // 0); return 0; } static int amlogic_hsgmii_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); struct device *dev = phy->dev.parent; u32 val; /* As of now only PHY_MODE_ETHERNET is supported */ if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; if (submode != PHY_INTERFACE_MODE_2500BASEX && submode != PHY_INTERFACE_MODE_SGMII) return -EOPNOTSUPP; /* Switch u3hsg combo to select HSGMII mode */ if (data->u3hsg_combo_reg) { val = readl(data->u3hsg_combo_reg); if (val != data->combo_hsg_sel_val) { dev_dbg(dev, "Switch combo PHY to select HSGMII, 0x%x -> 0x%x\n", val, data->combo_hsg_sel_val); writel(data->combo_hsg_sel_val, data->u3hsg_combo_reg); } } return 0; } static int amlogic_hsgmii_phy_calibrate(struct phy *phy) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); struct device *dev = phy->dev.parent; switch (data->speed) { case SPEED_10: case SPEED_100: case SPEED_1000: amlogic_hsgmii_phy_init_1g(data->regmap); break; case SPEED_2500: amlogic_hsgmii_phy_init_2p5g(data->regmap); break; } /* HSGMII reset */ amlogic_hsgmii_phy_reset(phy); if (amlogic_hsgmii_phy_poll_status(data->regmap, AML_PHY_HSG_PLL_INIT_DEBUG, HSG_PLL_INIT_DEBUG_PLL1_LOCK)) { dev_err(dev, "HSGMII_PLL1_LOCK timed-out"); return -ETIMEDOUT; } if (amlogic_hsgmii_phy_poll_status(data->regmap, AML_PHY_HSG_RX_CTRL_DEBUG, HSG_RX_CTRL_DEBUG_RX_FSM_READY)) { dev_err(dev, "HSGMII_RX_FSM_READY timed-out"); return -ETIMEDOUT; } if (amlogic_hsgmii_phy_poll_status(data->regmap, AML_PHY_HSG_RX_CTRL_DEBUG, HSG_RX_CTRL_DEBUG_UPCRX_CDRLOCK_SYNC)) { dev_err(dev, "HSGMII_UPCRX_CDRLOCK_SYNC timed-out"); return -ETIMEDOUT; } if (amlogic_hsgmii_phy_poll_status(data->regmap, AML_PHY_HSG_RX_CTRL_DEBUG, HSG_RX_CTRL_DEBUG_PCS_SYNC_CDC)) { dev_err(dev, "HSGMII_PCS_SYNC_CDC timed-out"); return -ETIMEDOUT; } return 0; } static int amlogic_hsgmii_phy_trim(struct phy *phy) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); struct device *dev = phy->dev.parent; u32 raw, val, otp_val; if (!data->hsg_trim_reg) return -EINVAL; raw = readl(data->hsg_trim_reg); if (!(raw & BIT(6))) { dev_err(dev, "No HSGMII PHY TX impedance trim? Skip.\n"); } else { regmap_read(data->regmap, AML_PHY_HSG_TX_CFG2, &val); val &= (~HSG_UPCTX_PDIO_MAIN_MASK) & (~HSG_UPCTX_PUIO_MAIN_MASK); /* set upctx_pdio_main in tx_cfg2. */ otp_val = (raw & (u32)GENMASK(2, 0)) >> 0; val |= (otp_val << HSG_UPCTX_PDIO_MAIN_SHIFT); /* set upctx_puio_main in tx_cfg2. */ otp_val = (raw & (u32)GENMASK(5, 3)) >> 3; val |= (otp_val << HSG_UPCTX_PUIO_MAIN_SHIFT); regmap_write(data->regmap, AML_PHY_HSG_TX_CFG2, val); } if (!(raw & BIT(12))) { dev_err(dev, "No HSGMII PHY TX amplitude trim? Skip.\n"); } else { /* set upctx_boost_ctrl in tx_cfg2. */ otp_val = (raw & (u32)GENMASK(11, 7)) >> 7; regmap_read(data->regmap, AML_PHY_HSG_TX_CFG2, &val); val &= ~HSG_UPCTX_BOOST_CTRL_MASK; regmap_write(data->regmap, AML_PHY_HSG_TX_CFG2, val | (otp_val << HSG_UPCTX_BOOST_CTRL_SHIFT)); /* set upctx_boost_en in tx_cfg0. */ regmap_read(data->regmap, AML_PHY_HSG_TX_CFG0, &val); regmap_write(data->regmap, AML_PHY_HSG_TX_CFG0, val | HSG_UPCTX_BOOST_EN); /* set upctx_boost_en in tx_cfg1. */ regmap_read(data->regmap, AML_PHY_HSG_TX_CFG1, &val); regmap_write(data->regmap, AML_PHY_HSG_TX_CFG1, val | HSG_UPCTX_BOOST_EN); } if (!(raw & BIT(17))) { dev_err(dev, "No HSGMII PHY RX impedance trim? Skip.\n"); } else { /* set usb3_upc_reg_dxxx in rx_cfg1. */ otp_val = (raw & (u32)GENMASK(16, 13)) >> 13; regmap_read(data->regmap, AML_PHY_HSG_RX_CFG1, &val); val &= ~HSG_DCHA_AFE_UPCRX_MASK; regmap_write(data->regmap, AML_PHY_HSG_RX_CFG1, val | (otp_val << HSG_DCHA_AFE_UPCRX_SHIFT)); } /* Hardware fix config: disable upctx_boost and de_emph */ regmap_read(data->regmap, AML_PHY_HSG_TX_CFG0, &val); val &= ~(HSG_UPCTX_BOOST_EN | HSG_UPCTX_SEL_PDR_POST); regmap_write(data->regmap, AML_PHY_HSG_TX_CFG0, val); regmap_read(data->regmap, AML_PHY_HSG_TX_CFG1, &val); val &= ~(HSG_UPCTX_BOOST_EN | HSG_UPCTX_SEL_PDR_POST); regmap_write(data->regmap, AML_PHY_HSG_TX_CFG1, val); return 0; } static int amlogic_hsgmii_phy_power_on(struct phy *phy) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); struct device *dev = phy->dev.parent; int ret; /* HSGMII PMA default parameters optimization */ /* RX */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG1, 0xE9943075); /* reg_dcha_afe */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG8, 0x5C144268); /* reg_dcha_dfe_h */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG9, 0x2C0014D4); /* reg_dchd_cdr_h */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG2, 0x54164268); /* reg_dcha_dfe */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG3, 0x2C001CD4); /* reg_dchd_cdr */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG4, 0x2011104F); /* reg_dchd_eq */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG0, 0x00007600); /* reg_misc */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG7, 0x0078E80A); /* upcrx_eq_byp_val */ /* TX */ regmap_write(data->regmap, AML_PHY_HSG_TX_CFG0, 0x001D3C5A); /* 2.5G */ regmap_write(data->regmap, AML_PHY_HSG_TX_CFG1, 0x001DFC5A); /* 1G */ /* FSM ctrl */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_BYPASS_PLL_CTRL, HSG_BYPASS_PLL_CTRL_DEBUG_MODE, 0); if (data->psl_mode) { /* PHY Sync Link mode */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, HSG_INIT_CTRL_INITIAL_MODE, HSG_INIT_CTRL_INITIAL_MODE); /* Config timer to wait phy speed stable */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, HSG_INIT_CTRL_SPEED_DETECT_TIMER_MASK, HSG_WAIT_SPEED_DETECT_TIMER << HSG_INIT_CTRL_SPEED_DETECT_TIMER_SHIFT); /* Force enable wait speed stable */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, HSG_INIT_CTRL_WAIT_SPEED_EN, HSG_INIT_CTRL_WAIT_SPEED_EN); /* FIXME: * Software assert phy_link_up, otherwise, mac dma reset * will fail and hsgmii_mode_rst_n will not release. */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, HSG_INIT_CTRL_PHY_LINK_STATUS, HSG_INIT_CTRL_PHY_LINK_STATUS); } else { /* Fast Link mode */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_INIT_CTRL, HSG_INIT_CTRL_INITIAL_MODE, 0); if (data->rx_fsm_restart) aml_regmap_update_bits(data->regmap, AML_PHY_HSG_RX_FSM_CTRL, HSG_RX_FSM_CTRL_RESTART_MODE, HSG_RX_FSM_CTRL_RESTART_MODE); if (data->pll_fsm_restart) aml_regmap_update_bits(data->regmap, AML_PHY_HSG_PLL_FSM_CTRL, HSG_PLL_FSM_CTRL_RESTART_MODE, HSG_PLL_FSM_CTRL_RESTART_MODE); } ret = amlogic_hsgmii_phy_trim(phy); if (ret) dev_err(dev, "failed to trim HSGMII PHY: %d\n", ret); data->power_on = true; return 0; } static int amlogic_hsgmii_phy_power_off(struct phy *phy) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); /* bypass FSM ctrl */ aml_regmap_update_bits(data->regmap, AML_PHY_HSG_BYPASS_PLL_CTRL, HSG_BYPASS_PLL_CTRL_DEBUG_MODE, HSG_BYPASS_PLL_CTRL_DEBUG_MODE); /* PLL */ regmap_write(data->regmap, AML_PHY_HSG_PLL0_HCSL_H_REG0, 0x80899788); /* 2.5G */ regmap_write(data->regmap, AML_PHY_HSG_PLL0_H_REG0, 0x02EE55E9); regmap_write(data->regmap, AML_PHY_HSG_PLL0_H_REG1, 0x002A4085); regmap_write(data->regmap, AML_PHY_HSG_PLL1_H_REG0, 0x40000071); regmap_write(data->regmap, AML_PHY_HSG_PLL1_H_REG1, 0x1842A320); regmap_write(data->regmap, AML_PHY_HSG_PLL1_H_REG2, 0x00000050); regmap_write(data->regmap, AML_PHY_HSG_PLL0_HCSL_L_REG0, 0x80899788); /* 1G */ regmap_write(data->regmap, AML_PHY_HSG_PLL0_L_REG0, 0x01F455E9); regmap_write(data->regmap, AML_PHY_HSG_PLL0_L_REG1, 0x0009E7E5); regmap_write(data->regmap, AML_PHY_HSG_PLL1_L_REG0, 0x40000071); regmap_write(data->regmap, AML_PHY_HSG_PLL1_L_REG1, 0x1842A320); regmap_write(data->regmap, AML_PHY_HSG_PLL1_L_REG2, 0x00000050); /* RX */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG4, 0x00000000); regmap_write(data->regmap, AML_PHY_HSG_RX_CFG9, 0x00000000); /* 2.5G */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG8, 0x00000200); regmap_write(data->regmap, AML_PHY_HSG_RX_CFG3, 0x00000000); /* 1G */ regmap_write(data->regmap, AML_PHY_HSG_RX_CFG2, 0x00000200); regmap_write(data->regmap, AML_PHY_HSG_RX_CFG1, 0x00000075); regmap_write(data->regmap, AML_PHY_HSG_RX_CFG0, 0x0000F600); /* TX */ regmap_write(data->regmap, AML_PHY_HSG_TX_CFG0, 0x001800B0); /* 2.5G */ regmap_write(data->regmap, AML_PHY_HSG_TX_CFG1, 0x001800B0); /* 1G */ data->power_on = false; return 0; } static int amlogic_hsgmii_phy_set_speed(struct phy *phy, int speed) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); if (speed != data->speed) data->speed = speed; return amlogic_hsgmii_phy_calibrate(phy); } static int amlogic_hsgmii_phy_init(struct phy *phy) { struct amlogic_hsgmii_phy_data *data = phy_get_drvdata(phy); int ret; ret = clk_prepare_enable(data->hsg_general_clk); if (ret) return ret; return amlogic_hsgmii_phy_reset(phy); } static int amlogic_hsgmii_phy_exit(struct phy *phy) { return amlogic_hsgmii_phy_reset(phy); } static const struct phy_ops amlogic_hsgmii_phy_ops = { .init = amlogic_hsgmii_phy_init, .exit = amlogic_hsgmii_phy_exit, .reset = amlogic_hsgmii_phy_reset, .power_on = amlogic_hsgmii_phy_power_on, .power_off = amlogic_hsgmii_phy_power_off, .set_speed = amlogic_hsgmii_phy_set_speed, .calibrate = amlogic_hsgmii_phy_calibrate, .set_mode = amlogic_hsgmii_phy_set_mode, .owner = THIS_MODULE, }; static const struct regmap_config amlogic_hsgmii_phy_regmap_cfg = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .use_relaxed_mmio = true, .disable_locking = true, }; static void amlogic_serdes_config_dt(struct platform_device *pdev, struct amlogic_hsgmii_phy_data *data) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; void __iomem *addr = NULL; unsigned int reg; data->psl_mode = of_property_read_bool(np, "amlogic,psl-mode"); data->rx_fsm_restart = of_property_read_bool(np, "amlogic,rx-fsm-restart"); data->pll_fsm_restart = of_property_read_bool(np, "amlogic,pll-fsm-restart"); if (data->psl_mode && (data->rx_fsm_restart || data->pll_fsm_restart)) dev_warn(dev, "rx_fsm_restart and pll_fsm_restart have no effect in PSL mode\n"); if (of_property_read_u32(np, "u3hsg-combo-reg", ®) == 0) { hsg_debug("u3hsg combo reg=0x%x\n", reg); addr = devm_ioremap(dev, reg, 4); if (IS_ERR_OR_NULL(addr)) { dev_err(dev, "failed to request u3hsg combo reg\n"); data->u3hsg_combo_reg = NULL; } else { data->u3hsg_combo_reg = addr; if (of_property_read_u32(np, "combo-hsg-sel-val", &data->combo_hsg_sel_val) != 0) data->combo_hsg_sel_val = 0; hsg_debug("combo_hsg_sel_val=0x%x\n", data->combo_hsg_sel_val); } } else { data->u3hsg_combo_reg = NULL; } if (of_property_read_u32(np, "hsg-trim-reg", ®) == 0) { hsg_debug("hsg trim reg=0x%x\n", reg); addr = devm_ioremap(dev, reg, 4); if (IS_ERR_OR_NULL(addr)) { dev_err(dev, "failed to request hsg trim reg\n"); data->hsg_trim_reg = NULL; } else { data->hsg_trim_reg = addr; } } else { data->hsg_trim_reg = NULL; } } static int amlogic_hsgmii_phy_probe(struct platform_device *pdev) { struct amlogic_hsgmii_phy_data *data; struct device *dev = &pdev->dev; struct phy *phy; struct phy_provider *provider; void __iomem *base; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); data->hsg_general_clk = devm_clk_get(dev, "hsg_general"); if (IS_ERR(data->hsg_general_clk)) return PTR_ERR(data->hsg_general_clk); data->hsg_reset = devm_reset_control_get_exclusive(dev, "hsg_rst"); if (IS_ERR(data->hsg_reset)) return PTR_ERR(data->hsg_reset); data->hsg_apb_reset = devm_reset_control_get_exclusive(dev, "hsg_apb_rst"); if (IS_ERR(data->hsg_apb_reset)) return PTR_ERR(data->hsg_apb_reset); data->regmap = devm_regmap_init_mmio(dev, base, &amlogic_hsgmii_phy_regmap_cfg); if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); data->power_on = false; data->speed = SPEED_UNKNOWN; amlogic_serdes_config_dt(pdev, data); phy = devm_phy_create(dev, NULL, &amlogic_hsgmii_phy_ops); if (IS_ERR(phy)) return PTR_ERR(phy); provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (IS_ERR(provider)) return PTR_ERR(provider); phy_set_drvdata(phy, data); dev_set_drvdata(dev, data); return 0; } #ifdef HSG_PHY_DEBUG #define HSG_PRINT_REG(var, ex) do { \ int _ret = snprintf(buf + len, PAGE_SIZE - len, \ "\n=== " #ex " (0x%03x) = 0x%08x ===\n", \ AML_PHY_HSG_##ex, (stats->var).val); \ len += _ret; \ } while (0) #define __F(var, fld) { #fld, ((stats->var).reg).fld } #define __F_2(val, f1, f2) __F(val, f1), __F(val, f2) #define __F_3(val, f1, f2, f3) __F_2(val, f1, f2), __F(val, f3) #define __F_4(val, f1, f2, f3, f4) __F_3(val, f1, f2, f3), __F(val, f4) #define __F_5(val, f1, f2, f3, f4, f5) __F_4(val, f1, f2, f3, f4), __F(val, f5) #define __F_6(val, f1, f2, f3, f4, f5, f6) __F_5(val, f1, f2, f3, f4, f5), __F(val, f6) #define __F_7(val, f1, f2, f3, f4, f5, f6, f7) __F_6(val, f1, f2, f3, f4, f5, f6), __F(val, f7) #define HSG_PRINT_FIELDS(...) do { \ const struct { const char *name; u32 val; } _f[] = { __VA_ARGS__ }; \ int _ret; \ int _i; \ for (_i = 0; _i < ARRAY_SIZE(_f); _i++) { \ _ret = snprintf(buf + len, PAGE_SIZE - len, \ " %-24s = %x\n", _f[_i].name, _f[_i].val); \ len += _ret; \ } \ } while (0) static void hsg_update_debug_stats(struct amlogic_hsgmii_phy_data *data) { struct hsg_phy_stats *stats = &data->hsg_stats; const struct { unsigned int reg; u32 *val; } hsg_regs[] = { { AML_PHY_HSG_RX_CFG1, &stats->rx_cfg1 }, { AML_PHY_HSG_TX_CFG0, &stats->tx_cfg0 }, { AML_PHY_HSG_TX_CFG1, &stats->tx_cfg1 }, { AML_PHY_HSG_BYPASS_PLL_CTRL, &stats->bypass_pll_ctrl }, { AML_PHY_HSG_TX_CFG2, &stats->tx_cfg2.val }, { AML_PHY_HSG_INIT_CTRL, &stats->init_ctrl.val }, { AML_PHY_HSG_MAC_STATUS, &stats->mac_status.val }, { AML_PHY_HSG_INIT_DEBUG, &stats->init_dbg.val }, { AML_PHY_HSG_PLL_INIT_DEBUG, &stats->pll_dbg.val }, { AML_PHY_HSG_RX_CTRL_DEBUG, &stats->rx_ctrl_dbg.val }, { AML_PHY_HSG_RX_DEBUG, &stats->rx_dbg.val }, }; int i; for (i = 0; i < ARRAY_SIZE(hsg_regs); i++) regmap_read(data->regmap, hsg_regs[i].reg, hsg_regs[i].val); } static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct amlogic_hsgmii_phy_data *data = dev_get_drvdata(dev); struct hsg_phy_stats *stats = &data->hsg_stats; ssize_t len = 0; u32 val; hsg_update_debug_stats(data); len += snprintf(buf + len, PAGE_SIZE - len, "=== Global Config ===\n"); len += snprintf(buf + len, PAGE_SIZE - len, "Power Status: %s\n", data->power_on ? "ON" : "OFF"); len += snprintf(buf + len, PAGE_SIZE - len, "Speed: %d Mbps\n", data->speed); len += snprintf(buf + len, PAGE_SIZE - len, "PSL Mode: %s\n", data->psl_mode ? "PHY Sync" : "Fast Link"); len += snprintf(buf + len, PAGE_SIZE - len, "Rx Fsm Restart Mode: %s\n", data->rx_fsm_restart ? "Y" : "N"); len += snprintf(buf + len, PAGE_SIZE - len, "PLL Fsm Restart Mode: %s\n", data->pll_fsm_restart ? "Y" : "N"); len += snprintf(buf + len, PAGE_SIZE - len, "PLL Fsm Control: %s\n", (stats->bypass_pll_ctrl & HSG_BYPASS_PLL_CTRL_DEBUG_MODE) ? "Disable" : "Enable"); if (data->u3hsg_combo_reg) { val = readl(data->u3hsg_combo_reg); len += snprintf(buf + len, PAGE_SIZE - len, "Combo Select HSG: %s\n", (val == data->combo_hsg_sel_val) ? "Y" : "N <- error"); } len += snprintf(buf + len, PAGE_SIZE - len, "=== Trim Config ===\n"); len += snprintf(buf + len, PAGE_SIZE - len, "RX impedance: %x\n", (u32)(stats->rx_cfg1 & HSG_DCHA_AFE_UPCRX_MASK) >> HSG_DCHA_AFE_UPCRX_SHIFT); len += snprintf(buf + len, PAGE_SIZE - len, "upctx_boost_en(1G mode): %s\n", (stats->tx_cfg1 & HSG_UPCTX_BOOST_EN) ? "Y" : "N"); len += snprintf(buf + len, PAGE_SIZE - len, "upctx_boost_en(2.5G mode): %s\n", (stats->tx_cfg0 & HSG_UPCTX_BOOST_EN) ? "Y" : "N"); HSG_PRINT_FIELDS(__F_3(tx_cfg2, upctx_pdio_main, upctx_puio_main, upctx_boost_ctrl), ); len += snprintf(buf + len, PAGE_SIZE - len, "upctx_sel_pdr_post(1G mode): %s\n", (stats->tx_cfg1 & HSG_UPCTX_SEL_PDR_POST) ? "Y" : "N"); len += snprintf(buf + len, PAGE_SIZE - len, "upctx_sel_pdr_post(2.5G mode): %s\n", (stats->tx_cfg0 & HSG_UPCTX_SEL_PDR_POST) ? "Y" : "N"); /* INIT CTRL */ HSG_PRINT_REG(init_ctrl, INIT_CTRL); HSG_PRINT_FIELDS(__F_7(init_ctrl, init_reset, phy_link_status, hsgmii_speed_en, hsgmii_speed, initial_mode, wait_speed_en, speed_detect_timer), ); /* MAC Status */ HSG_PRINT_REG(mac_status, MAC_STATUS); HSG_PRINT_FIELDS(__F_6(mac_status, rx_fsm_ready, pcs_acquired_sync, sgmii_link_spd, pcs_ewrap, pcs_en_cdet, pcs_lck_ref), ); /* INIT Debug */ HSG_PRINT_REG(init_dbg, INIT_DEBUG); HSG_PRINT_FIELDS(__F_6(init_dbg, hsgmii_mode_rst_n, hsgmii_clk_en, phy_link_status_final, hsgmii_speed, phy_speed_final, hsi_cstate), ); /* PLL Debug */ HSG_PRINT_REG(pll_dbg, PLL_INIT_DEBUG); HSG_PRINT_FIELDS(__F_5(pll_dbg, pll0_lock, pll1_lock, pll0_fail_cnt, pll1_fail_cnt, pll_cstate), ); /* RX Ctrl Debug */ HSG_PRINT_REG(rx_ctrl_dbg, RX_CTRL_DEBUG); HSG_PRINT_FIELDS(__F_5(rx_ctrl_dbg, pcs_acquired_sync_cdc, upcrx_cdlock_sync, rx_fsm_ready, lock_fail_cnt, rx_fsm_cstate), ); /* RX Debug */ HSG_PRINT_REG(rx_dbg, RX_DEBUG); HSG_PRINT_FIELDS(__F_2(rx_dbg, upcrx_state, upcrx_dbg_eq), ); return len; } static DEVICE_ATTR_RO(status); static struct attribute *hsgmii_phy_dev_attrs[] = { &dev_attr_status.attr, NULL, }; ATTRIBUTE_GROUPS(hsgmii_phy_dev); #endif static const struct of_device_id amlogic_hsgmii_phy_of_match[] = { { .compatible = "amlogic,hsgmii-phy" }, { }, }; MODULE_DEVICE_TABLE(of, amlogic_hsgmii_phy_of_match); static struct platform_driver amlogic_hsgmii_phy_driver = { .probe = amlogic_hsgmii_phy_probe, .driver = { .name = "amlogic-hsgmii-phy", .of_match_table = amlogic_hsgmii_phy_of_match, #ifdef HSG_PHY_DEBUG .dev_groups = hsgmii_phy_dev_groups, #endif }, }; module_platform_driver(amlogic_hsgmii_phy_driver); MODULE_DESCRIPTION("Amlogic HSGMII PHY driver"); MODULE_LICENSE("GPL");