Index: sys/dev/cadence/if_cgem.c =================================================================== --- sys/dev/cadence/if_cgem.c +++ sys/dev/cadence/if_cgem.c @@ -1516,6 +1516,145 @@ sc->mii_media_active = mii->mii_media_active; } +#ifdef EXT_RESOURCES +/* + * Clock node driver for ethernet transmit clock in SiFive FU540 and FU740 + * SoCs. + */ +struct sifive_tx_clk_sc { + struct resource *mem_res; + clk_t pclk; + uint64_t freq; +}; + +#define TX_CLK_ID 0 +#define TX_CLK_NAME "tx_clk" + +static int +cgem_sifive_tx_clk_init(struct clknode *clk, device_t dev) +{ + int rid; + struct sifive_tx_clk_sc *sc = clknode_get_softc(clk); + + /* Get memory resource of GEMGXL Management block. */ + rid = 1; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "could not allocate memory resources.\n"); + return (ENOMEM); + } + + return (0); +} + +static int +cgem_sifive_tx_clk_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + struct sifive_tx_clk_sc *sc = clknode_get_softc(clk); + + *freq = sc->freq; + + return (0); +} + +static int +cgem_sifive_tx_clk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *done) +{ + struct sifive_tx_clk_sc *sc = clknode_get_softc(clk); + + switch (*fout) { + case 2500000: + case 25000000: + WR4(sc, CGEM_MGMT_TX_CLK_SEL, CGEM_MGMT_TX_CLK_SEL_MII); + break; + case 125000000: + WR4(sc, CGEM_MGMT_TX_CLK_SEL, CGEM_MGMT_TX_CLK_SEL_GMII); + break; + default: + printf("%s: invalid frequency: %lu\n", __func__, *fout); + return (EINVAL); + } + + sc->freq = *fout; + *done = 1; + + return (0); +} + +static clknode_method_t sifive_tx_clk_clknode_methods[] = { + CLKNODEMETHOD(clknode_init, cgem_sifive_tx_clk_init), + CLKNODEMETHOD(clknode_recalc_freq, cgem_sifive_tx_clk_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, cgem_sifive_tx_clk_set_freq), + + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(sifive_tx_clk_clknode, sifive_tx_clk_clknode_class, + sifive_tx_clk_clknode_methods, sizeof(struct sifive_tx_clk_sc), + clknode_class); + +static int +cgem_sifive_clk_setup(struct cgem_softc *sc) +{ + device_t dev = sc->dev; + struct clkdom *clkdom; + struct clknode_init_def clkdef; + struct clknode *clk; + struct sifive_tx_clk_sc *csc; + + clkdom_xlock(NULL); + clkdom = clkdom_get_by_dev(dev); + clkdom_unlock(NULL); + if (clkdom == NULL) { + clkdom = clkdom_create(dev); + if (clkdom == NULL) { + device_printf(dev, "could not create clock domain.\n"); + return (-1); + } + + memset(&clkdef, 0, sizeof(clkdef)); + clkdef.id = TX_CLK_ID; + clkdef.name = TX_CLK_NAME; + clkdef.flags = CLK_NODE_STATIC_STRINGS; + + clk = clknode_create(clkdom, &sifive_tx_clk_clknode_class, + &clkdef); + if (clk == NULL) { + device_printf(dev, "could not create clock node.\n"); + return (-1); + } + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom)) { + device_printf(dev, + "could not finalise clock domain.\n"); + return (-1); + } + + /* Retrieve and enable GEMGXLPLL. */ + csc = clknode_get_softc(clk); + if (clk_get_by_ofw_name(dev, 0, "pclk", &csc->pclk) != 0) { + device_printf(dev, "could not retrieve pclk.\n"); + return (-1); + } + if (clk_enable(csc->pclk) != 0) { + device_printf(dev, "could not enable pclk.\n"); + return (-1); + } + } + + if (clk_get_by_id(dev, clkdom, TX_CLK_ID, &sc->ref_clk) != 0) { + device_printf(dev, "could not retrieve clock form domain.\n"); + return (-1); + } + + return (0); +} +#endif /* EXT_RESOURCES */ + static void cgem_add_sysctls(device_t dev) { @@ -1764,7 +1903,7 @@ else if (clk_enable(sc->ref_clk) != 0) device_printf(dev, "could not enable clock.\n"); } else if (hwtype == HWTYPE_SIFIVE) { - if (clk_get_by_ofw_name(dev, 0, "pclk", &sc->ref_clk) != 0) + if (cgem_sifive_clk_setup(sc) != 0) device_printf(dev, "could not retrieve reference clock.\n"); else if (clk_enable(sc->ref_clk) != 0) Index: sys/dev/cadence/if_cgem_hw.h =================================================================== --- sys/dev/cadence/if_cgem_hw.h +++ sys/dev/cadence/if_cgem_hw.h @@ -443,4 +443,17 @@ #endif }; +/* + * GEMGXL Management Control Registers for SiFive SoCs: + * + * Reference: SiFive FU740-C000 Manual v1p2, section 22.2. + */ +#define CGEM_MGMT_TX_CLK_SEL 0x000 +#define CGEM_MGMT_TX_CLK_SEL_GMII 0x0 +#define CGEM_MGMT_TX_CLK_SEL_MII 0x1 +#define CGEM_MGMT_CTRLSTAT_SPEED_MODE 0x020 +#define CGEM_MGMT_SPEED_MODE_10 0x0 +#define CGEM_MGMT_SPEED_MODE_100 0x1 +#define CGEM_MGMT_SPEED_MODE_1000 0x3 + #endif /* _IF_CGEM_HW_H_ */