#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/crc32.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/pm_runtime.h>
 #include "macb.h"
 
+/* This structure is only used for MACB on SiFive FU540 devices */
+struct sifive_fu540_macb_mgmt {
+       void __iomem *reg;
+       unsigned long rate;
+       struct clk_hw hw;
+};
+
+static struct sifive_fu540_macb_mgmt *mgmt;
+
 #define MACB_RX_BUFFER_SIZE    128
 #define RX_BUFFER_MULTIPLE     64  /* bytes */
 
        return 0;
 }
 
+static unsigned long fu540_macb_tx_recalc_rate(struct clk_hw *hw,
+                                              unsigned long parent_rate)
+{
+       return mgmt->rate;
+}
+
+static long fu540_macb_tx_round_rate(struct clk_hw *hw, unsigned long rate,
+                                    unsigned long *parent_rate)
+{
+       if (WARN_ON(rate < 2500000))
+               return 2500000;
+       else if (rate == 2500000)
+               return 2500000;
+       else if (WARN_ON(rate < 13750000))
+               return 2500000;
+       else if (WARN_ON(rate < 25000000))
+               return 25000000;
+       else if (rate == 25000000)
+               return 25000000;
+       else if (WARN_ON(rate < 75000000))
+               return 25000000;
+       else if (WARN_ON(rate < 125000000))
+               return 125000000;
+       else if (rate == 125000000)
+               return 125000000;
+
+       WARN_ON(rate > 125000000);
+
+       return 125000000;
+}
+
+static int fu540_macb_tx_set_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long parent_rate)
+{
+       rate = fu540_macb_tx_round_rate(hw, rate, &parent_rate);
+       if (rate != 125000000)
+               iowrite32(1, mgmt->reg);
+       else
+               iowrite32(0, mgmt->reg);
+       mgmt->rate = rate;
+
+       return 0;
+}
+
+static const struct clk_ops fu540_c000_ops = {
+       .recalc_rate = fu540_macb_tx_recalc_rate,
+       .round_rate = fu540_macb_tx_round_rate,
+       .set_rate = fu540_macb_tx_set_rate,
+};
+
+static int fu540_c000_clk_init(struct platform_device *pdev, struct clk **pclk,
+                              struct clk **hclk, struct clk **tx_clk,
+                              struct clk **rx_clk, struct clk **tsu_clk)
+{
+       struct clk_init_data init;
+       int err = 0;
+
+       err = macb_clk_init(pdev, pclk, hclk, tx_clk, rx_clk, tsu_clk);
+       if (err)
+               return err;
+
+       mgmt = devm_kzalloc(&pdev->dev, sizeof(*mgmt), GFP_KERNEL);
+       if (!mgmt)
+               return -ENOMEM;
+
+       init.name = "sifive-gemgxl-mgmt";
+       init.ops = &fu540_c000_ops;
+       init.flags = 0;
+       init.num_parents = 0;
+
+       mgmt->rate = 0;
+       mgmt->hw.init = &init;
+
+       *tx_clk = clk_register(NULL, &mgmt->hw);
+       if (IS_ERR(*tx_clk))
+               return PTR_ERR(*tx_clk);
+
+       err = clk_prepare_enable(*tx_clk);
+       if (err)
+               dev_err(&pdev->dev, "failed to enable tx_clk (%u)\n", err);
+       else
+               dev_info(&pdev->dev, "Registered clk switch '%s'\n", init.name);
+
+       return 0;
+}
+
+static int fu540_c000_init(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res)
+               return -ENODEV;
+
+       mgmt->reg = ioremap(res->start, resource_size(res));
+       if (!mgmt->reg)
+               return -ENOMEM;
+
+       return macb_init(pdev);
+}
+
+static const struct macb_config fu540_c000_config = {
+       .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO |
+               MACB_CAPS_GEM_HAS_PTP,
+       .dma_burst_length = 16,
+       .clk_init = fu540_c000_clk_init,
+       .init = fu540_c000_init,
+       .jumbo_max_len = 10240,
+};
+
 static const struct macb_config at91sam9260_config = {
        .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
        .clk_init = macb_clk_init,
        { .compatible = "cdns,emac", .data = &emac_config },
        { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config},
        { .compatible = "cdns,zynq-gem", .data = &zynq_config },
+       { .compatible = "sifive,fu540-macb", .data = &fu540_c000_config },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, macb_dt_ids);
 
 err_disable_clocks:
        clk_disable_unprepare(tx_clk);
+       clk_unregister(tx_clk);
        clk_disable_unprepare(hclk);
        clk_disable_unprepare(pclk);
        clk_disable_unprepare(rx_clk);
                pm_runtime_dont_use_autosuspend(&pdev->dev);
                if (!pm_runtime_suspended(&pdev->dev)) {
                        clk_disable_unprepare(bp->tx_clk);
+                       clk_unregister(bp->tx_clk);
                        clk_disable_unprepare(bp->hclk);
                        clk_disable_unprepare(bp->pclk);
                        clk_disable_unprepare(bp->rx_clk);