Commit 9368b8e2 authored by David S. Miller's avatar David S. Miller

Merge branch 'macb-SiFive-FU540-C000'

Yash Shah says:

====================
Add macb support for SiFive FU540-C000

On FU540, the management IP block is tightly coupled with the Cadence
MACB IP block. It manages many of the boundary signals from the MACB IP
This patchset controls the tx_clk input signal to the MACB IP. It
switches between the local TX clock (125MHz) and PHY TX clocks. This
is necessary to toggle between 1Gb and 100/10Mb speeds.

Future patches may add support for monitoring or controlling other IP
boundary signals.

This patchset is mostly based on work done by
Wesley Terpstra <wesley@sifive.com>

This patchset is based on Linux v5.2-rc1 and tested on HiFive Unleashed
board with additional board related patches needed for testing can be
found at dev/yashs/ethernet_v3 branch of:
https://github.com/yashshah7/riscv-linux.git

Change History:
V3:
- Revert "MACB_SIFIVE_FU540" config changes in Kconfig and driver code.
  The driver does not depend on SiFive GPIO driver.

V2:
- Change compatible string from "cdns,fu540-macb" to "sifive,fu540-macb"
- Add "MACB_SIFIVE_FU540" in Kconfig to support SiFive FU540 in macb
  driver. This is needed because on FU540, the macb driver depends on
  SiFive GPIO driver.
- Avoid writing the result of a comparison to a register.
- Fix the issue of probe fail on reloading the module reported by:
  Andreas Schwab <schwab@suse.de>
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d75d5f97 c218ad55
......@@ -15,8 +15,11 @@ Required properties:
Use "atmel,sama5d4-gem" for the GEM IP (10/100) available on Atmel sama5d4 SoCs.
Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC.
Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC.
Use "sifive,fu540-macb" for SiFive FU540-C000 SoC.
Or the generic form: "cdns,emac".
- reg: Address and length of the register set for the device
For "sifive,fu540-macb", second range is required to specify the
address and length of the registers for GEMGXL Management block.
- interrupts: Should contain macb interrupt
- phy-mode: See ethernet.txt file in the same directory.
- clock-names: Tuple listing input clock names.
......
......@@ -10,6 +10,7 @@
#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>
......@@ -40,6 +41,15 @@
#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 */
......@@ -3946,6 +3956,116 @@ static int at91ether_init(struct platform_device *pdev)
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,
......@@ -4035,6 +4155,7 @@ static const struct of_device_id macb_dt_ids[] = {
{ .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);
......@@ -4242,6 +4363,7 @@ static int macb_probe(struct platform_device *pdev)
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);
......@@ -4276,6 +4398,7 @@ static int macb_remove(struct platform_device *pdev)
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment