Add the CHIP_TOP registers required to configure the lan969x core PLL, and map the CHIP_TOP target. They are used in a later patch of this series. Signed-off-by: Daniel Machon --- .../ethernet/microchip/sparx5/lan969x/lan969x.c | 1 + .../ethernet/microchip/sparx5/sparx5_main_regs.h | 82 ++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c index c02499d1b297a..df211e608056a 100644 --- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c @@ -13,6 +13,7 @@ static const struct sparx5_main_io_resource lan969x_main_iomap[] = { { TARGET_CPU, 0xc0000, 0 }, /* 0xe00c0000 */ { TARGET_FDMA, 0xc0400, 0 }, /* 0xe00c0400 */ { TARGET_GCB, 0x2010000, 1 }, /* 0xe2010000 */ + { TARGET_CHIP_TOP, 0x2020000, 1 }, /* 0xe2020000 */ { TARGET_QS, 0x2030000, 1 }, /* 0xe2030000 */ { TARGET_PTP, 0x2040000, 1 }, /* 0xe2040000 */ { TARGET_ANA_ACL, 0x2050000, 1 }, /* 0xe2050000 */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index 1bf7dabf622e6..071d2f013a195 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -26,6 +26,7 @@ enum sparx5_target { TARGET_ANA_L2 = 7, TARGET_ANA_L3 = 8, TARGET_ASM = 9, + TARGET_CHIP_TOP = 10, TARGET_CLKGEN = 11, TARGET_CPU = 12, TARGET_DEV10G = 17, @@ -2636,6 +2637,87 @@ extern const struct sparx5_regs *regs; #define ASM_RAM_INIT_RAM_CFG_HOOK_GET(x)\ FIELD_GET(ASM_RAM_INIT_RAM_CFG_HOOK, x) +/* LAN969X ONLY */ +/* CHIP_TOP:HW_STAT:HW_STAT */ +#define CHIP_TOP_HW_STAT \ + __REG(TARGET_CHIP_TOP, 0, 1, 92, 0, 1, 4, 0, 0, 1, 4) + +#define CHIP_TOP_HW_STAT_REFCLK_SEL BIT(1) +#define CHIP_TOP_HW_STAT_REFCLK_SEL_SET(x)\ + FIELD_PREP(CHIP_TOP_HW_STAT_REFCLK_SEL, x) +#define CHIP_TOP_HW_STAT_REFCLK_SEL_GET(x)\ + FIELD_GET(CHIP_TOP_HW_STAT_REFCLK_SEL, x) + +/* LAN969X ONLY */ +/* CHIP_TOP:CORE_PLL_CFG:CORE_PLL_CFG */ +#define CHIP_TOP_CORE_PLL_CFG \ + __REG(TARGET_CHIP_TOP, 0, 1, 212, 0, 1, 8, 0, 0, 1, 4) + +#define CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL GENMASK(29, 28) +#define CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL, x) +#define CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL, x) + +#define CHIP_TOP_CORE_PLL_CFG_DIVACK BIT(27) +#define CHIP_TOP_CORE_PLL_CFG_DIVACK_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_DIVACK, x) +#define CHIP_TOP_CORE_PLL_CFG_DIVACK_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_DIVACK, x) + +#define CHIP_TOP_CORE_PLL_CFG_NEWDIV BIT(26) +#define CHIP_TOP_CORE_PLL_CFG_NEWDIV_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_NEWDIV, x) +#define CHIP_TOP_CORE_PLL_CFG_NEWDIV_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_NEWDIV, x) + +#define CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE GENMASK(25, 23) +#define CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE, x) +#define CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE, x) + +#define CHIP_TOP_CORE_PLL_CFG_DIVQ GENMASK(22, 8) +#define CHIP_TOP_CORE_PLL_CFG_DIVQ_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_DIVQ, x) +#define CHIP_TOP_CORE_PLL_CFG_DIVQ_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_DIVQ, x) + +#define CHIP_TOP_CORE_PLL_CFG_DIVR GENMASK(7, 2) +#define CHIP_TOP_CORE_PLL_CFG_DIVR_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_DIVR, x) +#define CHIP_TOP_CORE_PLL_CFG_DIVR_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_DIVR, x) + +#define CHIP_TOP_CORE_PLL_CFG_ENA_CFG BIT(1) +#define CHIP_TOP_CORE_PLL_CFG_ENA_CFG_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_ENA_CFG, x) +#define CHIP_TOP_CORE_PLL_CFG_ENA_CFG_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_ENA_CFG, x) + +#define CHIP_TOP_CORE_PLL_CFG_LOCK_STAT BIT(0) +#define CHIP_TOP_CORE_PLL_CFG_LOCK_STAT_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_CFG_LOCK_STAT, x) +#define CHIP_TOP_CORE_PLL_CFG_LOCK_STAT_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_CFG_LOCK_STAT, x) + +/* LAN969X ONLY */ +/* CHIP_TOP:CORE_PLL_CFG:CORE_PLL_FREQ_CFG */ +#define CHIP_TOP_CORE_PLL_FREQ_CFG \ + __REG(TARGET_CHIP_TOP, 0, 1, 212, 0, 1, 8, 4, 0, 1, 4) + +#define CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI GENMASK(30, 24) +#define CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI, x) +#define CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI, x) + +#define CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF GENMASK(23, 0) +#define CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF_SET(x)\ + FIELD_PREP(CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF, x) +#define CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF_GET(x)\ + FIELD_GET(CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF, x) + /* SPARX5 ONLY */ /* CLKGEN:LCPLL1:LCPLL1_CORE_CLK_CFG */ #define CLKGEN_LCPLL1_CORE_CLK_CFG \ -- 2.34.1 sparx5_init_switchcore() ends by setting HSCH_RESET_CFG.CORE_ENA, so the switch core and queue system are already running when sparx5_init_coreclock() configures the core clock afterwards. That is fine for the dividers it sets on sparx5, but a later patch makes it also program the lan969x core PLL, which should be done before the queue system is enabled. Move the CORE_ENA write to mchp_sparx5_probe(), right after sparx5_init_coreclock(), so the core is enabled once its clock is set up. CORE_ENA also tells sparx5_init_switchcore() that the switch memories are already initialized. The switch is only reset once, when the reset controller probes, so after an unbind and bind CORE_ENA is still set and the core clock is configured with the queue system running. Signed-off-by: Daniel Machon --- drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 68c80c63f9087..93a7cb89493c4 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -471,9 +471,6 @@ static int sparx5_init_switchcore(struct sparx5 *sparx5) spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET); spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG); - /* Enable switch-core and queue system */ - spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG); - return 0; } @@ -928,6 +925,9 @@ static int mchp_sparx5_probe(struct platform_device *pdev) goto cleanup_config; } + /* Enable switch-core and queue system */ + spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG); + for (idx = 0; idx < sparx5->port_count; ++idx) { config = &configs[idx]; if (!config->node) -- 2.34.1 The lan969x core PLL is left at its 328.125 MHz default, while the PTP time of day (TOD) increment from sparx5_ptp_get_nominal_value() assumes a core clock skewed about 200 ppm up, as recommended for the high accuracy phase detectors. The TOD and the clock that drives the scheduler therefore run 200 ppm apart: a 1 ms taprio cycle takes about 1000.2 us. Program the core PLL to the skewed rate from sparx5_init_coreclock(), using the glitch free divider change documented for CORE_PLL_CFG. Select the dividers, and the matching TOD increment, from the reference clock read from the strapping pin. Leave the PLL alone if it is already configured, and disable the register configuration again if the change fails. Signed-off-by: Daniel Machon --- .../net/ethernet/microchip/sparx5/sparx5_main.c | 119 +++++++++++++++++++++ .../net/ethernet/microchip/sparx5/sparx5_main.h | 7 ++ drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c | 5 +- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 93a7cb89493c4..244974ffe496b 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -474,10 +474,120 @@ static int sparx5_init_switchcore(struct sparx5 *sparx5) return 0; } +struct lan969x_core_pll_dividers { + u32 divfi; + u32 divff; + u32 divr; + u32 divq; +}; + +static const struct lan969x_core_pll_dividers lan969x_core_pll_328mhz[] = { + [SPX5_CORE_CLOCK_REF_25MHZ] = { + .divfi = 64, .divff = 0xa36076, .divr = 3, .divq = 10, + }, + [SPX5_CORE_CLOCK_REF_39MHZ] = { + .divfi = 72, .divff = 0x83c361, .divr = 6, .divq = 10, + }, +}; + +static int lan969x_init_core_pll(struct sparx5 *sparx5) +{ + const struct lan969x_core_pll_dividers *div; + u32 hw_stat, pll_cfg, freq_cfg; + enum sparx5_core_clockref ref; + int err; + + hw_stat = spx5_rd(sparx5, CHIP_TOP_HW_STAT); + ref = CHIP_TOP_HW_STAT_REFCLK_SEL_GET(hw_stat) ? + SPX5_CORE_CLOCK_REF_39MHZ : SPX5_CORE_CLOCK_REF_25MHZ; + div = &lan969x_core_pll_328mhz[ref]; + sparx5->coreclockref = ref; + + /* Already configured by an earlier probe? */ + pll_cfg = spx5_rd(sparx5, CHIP_TOP_CORE_PLL_CFG); + freq_cfg = spx5_rd(sparx5, CHIP_TOP_CORE_PLL_FREQ_CFG); + if (CHIP_TOP_CORE_PLL_CFG_ENA_CFG_GET(pll_cfg) && + CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI_GET(freq_cfg) == div->divfi && + CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF_GET(freq_cfg) == div->divff) + return 0; + + spx5_rmw(CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI_SET(div->divfi) | + CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF_SET(div->divff), + CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFI | + CHIP_TOP_CORE_PLL_FREQ_CFG_DIVFF, + sparx5, CHIP_TOP_CORE_PLL_FREQ_CFG); + + /* Set the reference and output dividers, select the reference clock + * from the strapping pin and use fractional mode. + */ + spx5_rmw(CHIP_TOP_CORE_PLL_CFG_DIVQ_SET(div->divq) | + CHIP_TOP_CORE_PLL_CFG_DIVR_SET(div->divr) | + CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL_SET(0) | + CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE_SET(0), + CHIP_TOP_CORE_PLL_CFG_DIVQ | + CHIP_TOP_CORE_PLL_CFG_DIVR | + CHIP_TOP_CORE_PLL_CFG_REF_CLK_SEL | + CHIP_TOP_CORE_PLL_CFG_FILTER_RANGE, + sparx5, CHIP_TOP_CORE_PLL_CFG); + + /* Switch the PLL from its reset defaults to the register settings */ + spx5_rmw(CHIP_TOP_CORE_PLL_CFG_ENA_CFG_SET(1), + CHIP_TOP_CORE_PLL_CFG_ENA_CFG, + sparx5, CHIP_TOP_CORE_PLL_CFG); + + spx5_rmw(CHIP_TOP_CORE_PLL_CFG_NEWDIV_SET(1), + CHIP_TOP_CORE_PLL_CFG_NEWDIV, + sparx5, CHIP_TOP_CORE_PLL_CFG); + + err = read_poll_timeout(spx5_rd, + pll_cfg, + CHIP_TOP_CORE_PLL_CFG_DIVACK_GET(pll_cfg), + 100, + 10000, + false, + sparx5, + CHIP_TOP_CORE_PLL_CFG); + + spx5_rmw(CHIP_TOP_CORE_PLL_CFG_NEWDIV_SET(0), + CHIP_TOP_CORE_PLL_CFG_NEWDIV, + sparx5, CHIP_TOP_CORE_PLL_CFG); + + if (err) { + dev_err(sparx5->dev, + "Failed to change the core PLL dividers\n"); + goto err_disable; + } + + /* LOCK_STAT resets to 1, so sleep before the first read */ + err = read_poll_timeout(spx5_rd, + pll_cfg, + CHIP_TOP_CORE_PLL_CFG_LOCK_STAT_GET(pll_cfg), + 100, + 10000, + true, + sparx5, + CHIP_TOP_CORE_PLL_CFG); + if (err) { + dev_err(sparx5->dev, "Failed to lock the core PLL\n"); + goto err_disable; + } + + return 0; + +err_disable: + /* Make the next probe redo the change */ + spx5_rmw(CHIP_TOP_CORE_PLL_CFG_ENA_CFG_SET(0), + CHIP_TOP_CORE_PLL_CFG_ENA_CFG, + sparx5, CHIP_TOP_CORE_PLL_CFG); + + return err; +} + static int sparx5_init_coreclock(struct sparx5 *sparx5) { enum sparx5_core_clockfreq freq = sparx5->coreclock; u32 clk_div, clk_period, pol_upd_int, idx; + int err; /* Verify if core clock frequency is supported on target. * If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported @@ -573,6 +683,15 @@ static int sparx5_init_coreclock(struct sparx5 *sparx5) CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA, sparx5, CLKGEN_LCPLL1_CORE_CLK_CFG); } else { + if (freq != SPX5_CORE_CLOCK_328MHZ) { + dev_err(sparx5->dev, + "%d coreclock not supported\n", + freq); + return -EINVAL; + } + err = lan969x_init_core_pll(sparx5); + if (err) + return err; pol_upd_int = 820; // SPX5_CORE_CLOCK_328MHZ } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index ad29db91ce91e..f6316086e6ecb 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -266,6 +266,12 @@ struct sparx5_port { } tas_lists[SPX5_TAS_ENTRIES_PER_PORT]; }; +/* lan969x core PLL reference clock, from CHIP_TOP:HW_STAT.REFCLK_SEL */ +enum sparx5_core_clockref { + SPX5_CORE_CLOCK_REF_25MHZ, + SPX5_CORE_CLOCK_REF_39MHZ, +}; + enum sparx5_core_clockfreq { SPX5_CORE_CLOCK_DEFAULT, /* Defaults to the highest supported frequency */ SPX5_CORE_CLOCK_250MHZ, /* 250MHZ core clock frequency */ @@ -415,6 +421,7 @@ struct sparx5 { /* port structures are in net device */ struct sparx5_port *ports[SPX5_PORTS]; enum sparx5_core_clockfreq coreclock; + enum sparx5_core_clockref coreclockref; /* Statistics */ u32 num_stats; u32 num_ethtool_stats; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c index 9f61692b243b7..544e9513a9a53 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c @@ -62,7 +62,10 @@ static u64 sparx5_ptp_get_nominal_value(struct sparx5 *sparx5) res = 0x1FF0000000000000; break; case SPX5_CORE_CLOCK_328MHZ: - res = 0x18604697DD0F9B5B; + if (sparx5->coreclockref == SPX5_CORE_CLOCK_REF_25MHZ) + res = 0x186044FEF1EA9C10; + else + res = 0x18604697DD0F9B5B; break; case SPX5_CORE_CLOCK_500MHZ: res = 0x0FF8000000000000; -- 2.34.1 sparx5_ptp_adjfine() builds the new TOD increment from two tables: the nominal increment from sparx5_ptp_get_nominal_value(), and a one ppm step from sparx5_ptp_get_1ppm(), which it multiplies by the requested ppm. Nothing ties the two tables together, so they can get out of step. They already are for the lan969x 328 MHz core clock: the nominal increment is derived from the skewed rate, the one ppm step from the 328.125 MHz default. A 1 ppm request therefore moves the increment by 1.0002 ppm. Drop sparx5_ptp_get_1ppm() and scale the nominal increment directly with adjust_by_scaled_ppm(). The sparx5 core clocks are unchanged, as their two tables agree. Signed-off-by: Daniel Machon --- drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c | 49 +--------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c index 544e9513a9a53..e37635e8fd730 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c @@ -22,37 +22,6 @@ enum { PTP_PIN_ACTION_TOD }; -static u64 sparx5_ptp_get_1ppm(struct sparx5 *sparx5) -{ - /* Represents 1ppm adjustment in 2^59 format with 1.59687500000(625) - * 1.99609375000(500), 3.99218750000(250) as reference - * The value is calculated as following: - * (1/1000000)/((2^-59)/X) - */ - - u64 res = 0; - - switch (sparx5->coreclock) { - case SPX5_CORE_CLOCK_250MHZ: - res = 2301339409586; - break; - case SPX5_CORE_CLOCK_328MHZ: - res = 1756832768924; - break; - case SPX5_CORE_CLOCK_500MHZ: - res = 1150669704793; - break; - case SPX5_CORE_CLOCK_625MHZ: - res = 920535763834; - break; - default: - WARN(1, "Invalid core clock"); - break; - } - - return res; -} - static u64 sparx5_ptp_get_nominal_value(struct sparx5 *sparx5) { u64 res = 0; @@ -407,27 +376,13 @@ static int sparx5_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); struct sparx5 *sparx5 = phc->sparx5; unsigned long flags; - bool neg_adj = 0; u64 tod_inc; - u64 ref; if (!scaled_ppm) return 0; - if (scaled_ppm < 0) { - neg_adj = 1; - scaled_ppm = -scaled_ppm; - } - - tod_inc = sparx5_ptp_get_nominal_value(sparx5); - - /* The multiplication is split in 2 separate additions because of - * overflow issues. If scaled_ppm with 16bit fractional part was bigger - * than 20ppm then we got overflow. - */ - ref = sparx5_ptp_get_1ppm(sparx5) * (scaled_ppm >> 16); - ref += (sparx5_ptp_get_1ppm(sparx5) * (0xffff & scaled_ppm)) >> 16; - tod_inc = neg_adj ? tod_inc - ref : tod_inc + ref; + tod_inc = adjust_by_scaled_ppm(sparx5_ptp_get_nominal_value(sparx5), + scaled_ppm); spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); -- 2.34.1