[PATCH net-next] driver: cadence macb driver support acpi mode
From: xiaowu.ding
Date: Wed Aug 24 2022 - 08:14:20 EST
Uefi report the device info with acpi mode.But the macb driver
just support dts mode , So we need to add acpi mode with this driver.
We add the macb_acpi_support interface to get the clock rating from
the acpi.Because in the acpi mode ,the clock is controlled by the uefi,
and the driver will not modify the clock anymore.So we need get the
clock rating from the acpi
And we add macb_mdiobus_register interface ,and distinction the dts mode
and the acpi mode.Within the function, we add macb_acpi_mdiobus_register
function to realize the acpi mdio device register function.
And we also add macb_phylink_connect interface,and to distinction the dts
and acpi mode with the phy connection function.We add the
macb_acpi_phylink_connect function to realize the acpi phylink function.
Signed-off-by: xiaowu.ding <xiaowu.ding@xxxxxxxxxxxxxxx>
---
drivers/net/ethernet/cadence/macb.h | 46 ++++
drivers/net/ethernet/cadence/macb_main.c | 330 +++++++++++++++++++++--
2 files changed, 357 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 9c410f93a..a92299434 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -731,6 +731,33 @@
#define MACB_LSO_UFO_ENABLE 0x01
#define MACB_LSO_TSO_ENABLE 0x02
+/*Clk name define used for acpi mode*/
+#define MACB_SYSPCLOCK "pclk"
+#define MACB_SYSHCLOCK "hclk"
+#define MACB_TXCLOCK "tx_clk"
+#define MACB_RXCLOCK "rx_clk"
+#define MACB_TSCLOCK "tsu_clk"
+
+/*Get the pclk clock rate used for acpi*/
+#define MACB_GET_GET_PCLOCKRATE(MACB) \
+ ((MACB)->acpicfg.pclk_rate)
+
+/*Get the hclk clock rate used for acpi*/
+#define MACB_GET_GET_HCLOCKRATE(MACB) \
+ ((MACB)->acpicfg.hclk_rate)
+
+/*Get the tsuclk clock rate used for acpi*/
+#define MACB_GET_GET_TSUCLOCKRATE(MACB) \
+ ((MACB)->acpicfg.tsuclk_rate)
+
+/*Get the tsuclk clock rate used for acpi*/
+#define MACB_GET_GET_TXCLOCKRATE(MACB) \
+ ((MACB)->acpicfg.txclk_rate)
+
+/*Get the tsuclk clock rate used for acpi*/
+#define MACB_GET_GET_RXCLOCKRATE(MACB) \
+ ((MACB)->acpicfg.rxclk_rate)
+
/* Bit manipulation macros */
#define MACB_BIT(name) \
(1 << MACB_##name##_OFFSET)
@@ -1242,6 +1269,18 @@ struct ethtool_rx_fs_list {
unsigned int count;
};
+/* On ACPI platforms, clocks are controlled by firmware and/or
+ * ACPI, not by drivers.Need to store the clock value.
+ */
+struct macb_acpi_config {
+ u32 hclk_rate; /* amba clock rate*/
+ u32 pclk_rate; /* amba apb clock rate*/
+ u32 txclk_rate; /* tx clock rate*/
+ u32 rxclk_rate; /* rx clock rate*/
+ u32 tsuclk_rate; /* tx clock rate*/
+ bool acpi_enable; /* is acpi or not */
+};
+
struct macb {
void __iomem *regs;
bool native_io;
@@ -1324,6 +1363,7 @@ struct macb {
struct macb_pm_data pm_data;
const struct macb_usrio_config *usrio;
+ struct macb_acpi_config acpicfg;
};
#ifdef CONFIG_MACB_USE_HWSTAMP
@@ -1381,6 +1421,12 @@ static inline bool gem_has_ptp(struct macb *bp)
return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP);
}
+/* macb is support acpi or not */
+static inline bool macb_has_acpi(struct macb *bp)
+{
+ return bp->acpicfg.acpi_enable;
+}
+
/**
* struct macb_platform_data - platform data for MACB Ethernet used for PCI registration
* @pclk: platform clock
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 66c7d08d3..41116d4ee 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -38,6 +38,8 @@
#include <linux/pm_runtime.h>
#include <linux/ptp_classify.h>
#include <linux/reset.h>
+#include <linux/acpi.h>
+#include <linux/acpi_mdio.h>
#include "macb.h"
/* This structure is only used for MACB on SiFive FU540 devices */
@@ -748,14 +750,14 @@ static const struct phylink_mac_ops macb_phylink_ops = {
.mac_link_up = macb_mac_link_up,
};
-static bool macb_phy_handle_exists(struct device_node *dn)
+static bool macb_of_phy_handle_exists(struct device_node *dn)
{
dn = of_parse_phandle(dn, "phy-handle", 0);
of_node_put(dn);
return dn != NULL;
}
-static int macb_phylink_connect(struct macb *bp)
+static int macb_of_phylink_connect(struct macb *bp)
{
struct device_node *dn = bp->pdev->dev.of_node;
struct net_device *dev = bp->dev;
@@ -765,7 +767,7 @@ static int macb_phylink_connect(struct macb *bp)
if (dn)
ret = phylink_of_phy_connect(bp->phylink, dn, 0);
- if (!dn || (ret && !macb_phy_handle_exists(dn))) {
+ if (!dn || (ret && !macb_of_phy_handle_exists(dn))) {
phydev = phy_find_first(bp->mii_bus);
if (!phydev) {
netdev_err(dev, "no PHY found\n");
@@ -786,6 +788,166 @@ static int macb_phylink_connect(struct macb *bp)
return 0;
}
+#ifdef CONFIG_ACPI
+
+static bool macb_acpi_phy_handle_exists(struct fwnode_handle *fwnd)
+{
+ struct fwnode_handle *phy_node;
+ bool flag = false;
+ /* Only phy-handle is used for ACPI */
+ phy_node = fwnode_find_reference(fwnd, "phy-handle", 0);
+ flag = !IS_ERR_OR_NULL(phy_node);
+
+ if (flag)
+ fwnode_handle_put(phy_node);
+
+ return flag;
+}
+
+static int macb_acpi_phylink_connect(struct macb *bp)
+{
+ struct fwnode_handle *fwnd = bp->pdev->dev.fwnode;
+ struct net_device *dev = bp->dev;
+ struct phy_device *phydev;
+ int ret;
+
+ if (fwnd)
+ ret = phylink_fwnode_phy_connect(bp->phylink, fwnd, 0);
+
+ if (!fwnd || (ret && !macb_acpi_phy_handle_exists(fwnd))) {
+ phydev = phy_find_first(bp->mii_bus);
+ if (!phydev) {
+ netdev_err(dev, "no PHY found\n");
+ return -ENXIO;
+ }
+
+ /* attach the mac to the phy */
+ ret = phylink_connect_phy(bp->phylink, phydev);
+ }
+
+ if (ret) {
+ netdev_err(dev, "Could not attach PHY (%d)\n", ret);
+ return ret;
+ }
+
+ phylink_start(bp->phylink);
+
+ return 0;
+}
+
+static int macb_acpi_support(struct macb *bp)
+{
+ struct device *dev = &bp->pdev->dev;
+ struct macb_acpi_config *config = &bp->acpicfg;
+ int ret;
+ u32 property;
+
+ /*acpi must be report the pclk*/
+ property = 0;
+ ret = device_property_read_u32(dev, MACB_SYSPCLOCK, &property);
+ if (ret) {
+ dev_err(dev, "unable to obtain %s property\n", MACB_SYSPCLOCK);
+ return ret;
+ }
+
+ config->pclk_rate = property;
+
+ /*acpi must be report the hclk*/
+ property = 0;
+ ret = device_property_read_u32(dev, MACB_SYSHCLOCK, &property);
+ if (ret) {
+ dev_err(dev, "unable to obtain %s property\n", MACB_SYSHCLOCK);
+ return ret;
+ }
+
+ config->hclk_rate = property;
+
+ /*acpi optional report txclk */
+ property = 0;
+ ret = device_property_read_u32(dev, MACB_TXCLOCK, &property);
+ if (ret) {
+ dev_info(dev, "unable to obtain %s property\n", MACB_TXCLOCK);
+ property = 0;
+ }
+
+ config->txclk_rate = property;
+
+ /*acpi optional report rxclk */
+ property = 0;
+ ret = device_property_read_u32(dev, MACB_RXCLOCK, &property);
+ if (ret) {
+ dev_info(dev, "unable to obtain %s property\n", MACB_RXCLOCK);
+ property = 0;
+ }
+
+ config->rxclk_rate = property;
+
+ /*acpi optional report tsuclk */
+ property = 0;
+ ret = device_property_read_u32(dev, MACB_TSCLOCK, &property);
+ if (ret) {
+ dev_info(dev, "unable to obtain %s property\n", MACB_TSCLOCK);
+ property = 0;
+ }
+
+ config->tsuclk_rate = property;
+
+ return 0;
+}
+
+static int macb_acpi_mdiobus_register(struct macb *bp)
+{
+ struct platform_device *pdev = bp->pdev;
+ struct fwnode_handle *fwnode = pdev->dev.fwnode;
+ struct fwnode_handle *child;
+ u32 addr;
+ int ret;
+
+ if (!IS_ERR_OR_NULL(fwnode_find_reference(pdev->dev.fwnode, "fixed-link", 0)))
+ return mdiobus_register(bp->mii_bus);
+
+ fwnode_for_each_child_node(fwnode, child) {
+ ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr);
+ if (ret || addr >= PHY_MAX_ADDR)
+ continue;
+ ret = acpi_mdiobus_register(bp->mii_bus, fwnode);
+ if (!ret)
+ return ret;
+ break;
+ }
+
+ return mdiobus_register(bp->mii_bus);
+}
+
+#else
+
+static int macb_acpi_phylink_connect(struct macb *bp)
+{
+ return -EINVAL;
+}
+
+static int macb_acpi_support(struct macb *bp)
+{
+ return -EINVAL;
+}
+
+static int macb_acpi_mdiobus_register(struct macb *bp)
+{
+ return -EINVAL;
+}
+
+#endif
+
+static int macb_phylink_connect(struct macb *bp)
+{
+ if (likely(!macb_has_acpi(bp))) {
+ /* macb of device tree mode register mdiobus */
+ return macb_of_phylink_connect(bp);
+ }
+ /* macb acpi mode register mdiobus */
+ return macb_acpi_phylink_connect(bp);
+}
+
static void macb_get_pcs_fixed_state(struct phylink_config *config,
struct phylink_link_state *state)
{
@@ -851,7 +1013,7 @@ static int macb_mii_probe(struct net_device *dev)
return 0;
}
-static int macb_mdiobus_register(struct macb *bp)
+static int macb_of_mdiobus_register(struct macb *bp)
{
struct device_node *child, *np = bp->pdev->dev.of_node;
@@ -887,6 +1049,16 @@ static int macb_mdiobus_register(struct macb *bp)
return mdiobus_register(bp->mii_bus);
}
+static int macb_mdiobus_register(struct macb *bp)
+{
+ if (likely(!macb_has_acpi(bp))) {
+ /* macb of device tree mode register mdio bus */
+ return macb_of_mdiobus_register(bp);
+ }
+ /* macb acpi mode register mdio bus */
+ return macb_acpi_mdiobus_register(bp);
+}
+
static int macb_mii_init(struct macb *bp)
{
int err = -ENXIO;
@@ -2578,7 +2750,12 @@ static void macb_reset_hw(struct macb *bp)
static u32 gem_mdc_clk_div(struct macb *bp)
{
u32 config;
- unsigned long pclk_hz = clk_get_rate(bp->pclk);
+ unsigned long pclk_hz;
+
+ if (unlikely(macb_has_acpi(bp)))
+ pclk_hz = MACB_GET_GET_PCLOCKRATE(bp);
+ else
+ pclk_hz = clk_get_rate(bp->pclk);
if (pclk_hz <= 20000000)
config = GEM_BF(CLK, GEM_CLK_DIV8);
@@ -3263,6 +3440,8 @@ static unsigned int gem_get_tsu_rate(struct macb *bp)
{
struct clk *tsu_clk;
unsigned int tsu_rate;
+ if (unlikely(macb_has_acpi(bp)))
+ return MACB_GET_GET_TSUCLOCKRATE(bp);
tsu_clk = devm_clk_get(&bp->pdev->dev, "tsu_clk");
if (!IS_ERR(tsu_clk))
@@ -3865,7 +4044,6 @@ static void macb_clks_disable(struct clk *pclk, struct clk *hclk, struct clk *tx
{ .clk = hclk, },
{ .clk = tx_clk },
};
-
clk_bulk_disable_unprepare(ARRAY_SIZE(clks), clks);
}
@@ -4811,6 +4989,94 @@ static const struct of_device_id macb_dt_ids[] = {
MODULE_DEVICE_TABLE(of, macb_dt_ids);
#endif /* CONFIG_OF */
+#ifdef CONFIG_ACPI
+
+static int jmnd_mac_clk_init(struct platform_device *pdev, struct clk **pclk,
+ struct clk **hclk, struct clk **tx_clk,
+ struct clk **rx_clk, struct clk **tsu_clk)
+{
+ /*On ACPI platforms, clocks are controlled by firmware and/or
+ * ACPI, not configure by drivers.So here need to dummy here.
+ */
+ *pclk = NULL;
+ *hclk = NULL;
+ *tx_clk = NULL;
+ *rx_clk = NULL;
+ *tsu_clk = NULL;
+ dev_info(&pdev->dev, "Jmnd Mac clock dummpy init.\n");
+ return 0;
+}
+
+static const struct macb_config jmnd_gem_rgmii_config = {
+ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_SG_DISABLED |
+ MACB_CAPS_JUMBO,
+ .dma_burst_length = 16,
+ .clk_init = jmnd_mac_clk_init,
+ .init = macb_init,
+ .jumbo_max_len = 10240,
+ .usrio = &macb_default_usrio
+};
+
+static const struct macb_config jmnd_gem_rmii_config = {
+ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_JUMBO,
+ .dma_burst_length = 16,
+ .clk_init = jmnd_mac_clk_init,
+ .init = macb_init,
+ .jumbo_max_len = 10240,
+ .usrio = &macb_default_usrio,
+};
+
+static const struct acpi_device_id macb_acpi_ids[] = {
+ {
+ .id = "JAMC00B0",
+ .driver_data = (kernel_ulong_t)&jmnd_gem_rgmii_config
+ },
+ {
+ .id = "JAMC00BA",
+ .driver_data = (kernel_ulong_t)&jmnd_gem_rmii_config
+ },
+ {
+ },
+};
+
+MODULE_DEVICE_TABLE(acpi, macb_acpi_ids);
+
+static void macb_acpi_gdata(struct platform_device *pdev, int (**init)(struct platform_device *),
+ int (**clk_init)(struct platform_device *, struct clk **, struct clk **,
+ struct clk **, struct clk **, struct clk **),
+ const struct macb_config **macb_config)
+{
+ const struct acpi_device_id *match;
+
+ match = acpi_match_device(macb_acpi_ids, &pdev->dev);
+ if (match && match->driver_data) {
+ *macb_config = (struct macb_config *)match->driver_data;
+ *clk_init = (*macb_config)->clk_init;
+ *init = (*macb_config)->init;
+ }
+}
+
+#else
+
+static void macb_acpi_gdata(struct platform_device *pdev, int (**init)(struct platform_device *),
+ int (**clk_init)(struct platform_device *, struct clk **, struct clk **,
+ struct clk **, struct clk **, struct clk **),
+ const struct macb_config **macb_config)
+{
+}
+
+#endif
+
+/* config acpi mode ,dts priority high*/
+static inline void macb_set_acpi_mode(struct macb *bp)
+{
+ struct device *dev = &bp->pdev->dev;
+
+ bp->acpicfg.acpi_enable = dev_of_node(dev) ? false :
+ (is_acpi_node(dev->fwnode) ? true : false);
+ wmb();/* drain writebuffer */
+}
+
static const struct macb_config default_gem_config = {
.caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE |
MACB_CAPS_JUMBO |
@@ -4830,6 +5096,7 @@ static int macb_probe(struct platform_device *pdev)
struct clk **) = macb_config->clk_init;
int (*init)(struct platform_device *) = macb_config->init;
struct device_node *np = pdev->dev.of_node;
+ struct fwnode_handle *fwnodep = pdev->dev.fwnode;
struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
struct clk *tsu_clk = NULL;
unsigned int queue_mask, num_queues;
@@ -4845,7 +5112,7 @@ static int macb_probe(struct platform_device *pdev)
if (IS_ERR(mem))
return PTR_ERR(mem);
- if (np) {
+ if (likely(np)) {
const struct of_device_id *match;
match = of_match_node(macb_dt_ids, np);
@@ -4855,10 +5122,15 @@ static int macb_probe(struct platform_device *pdev)
init = macb_config->init;
}
}
+ /*add gem support acpi*/
+ else if (is_acpi_node(fwnodep))
+ macb_acpi_gdata(pdev, &init, &clk_init, &macb_config);
- err = clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk);
- if (err)
- return err;
+ if (clk_init) {
+ err = clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk);
+ if (err)
+ return err;
+ }
pm_runtime_set_autosuspend_delay(&pdev->dev, MACB_PM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
@@ -4902,9 +5174,16 @@ static int macb_probe(struct platform_device *pdev)
if (macb_config)
bp->jumbo_max_len = macb_config->jumbo_max_len;
+ /* macb set acpi mode is enabled */
+ macb_set_acpi_mode(bp);
+
+ if (macb_has_acpi(bp))
+ macb_acpi_support(bp);
+
bp->wol = 0;
- if (of_get_property(np, "magic-packet", NULL))
+ if (device_property_present(&pdev->dev, "magic-packet"))
bp->wol |= MACB_WOL_HAS_MAGIC_PACKET;
+
device_set_wakeup_capable(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET);
bp->usrio = macb_config->usrio;
@@ -4951,15 +5230,13 @@ static int macb_probe(struct platform_device *pdev)
if (bp->caps & MACB_CAPS_NEEDS_RSTONUBR)
bp->rx_intr_mask |= MACB_BIT(RXUBR);
- err = of_get_ethdev_address(np, bp->dev);
- if (err == -EPROBE_DEFER)
- goto err_out_free_netdev;
- else if (err)
+ if (device_get_ethdev_address(&pdev->dev, dev))
macb_get_hwaddr(bp);
- err = of_get_phy_mode(np, &interface);
- if (err)
- /* not found in DT, MII by default */
+ /*add gem support acpi*/
+ interface = device_get_phy_mode(&pdev->dev);
+ if (interface < 0)
+ /* not found in DT and ACPI , MII by default */
bp->phy_interface = PHY_INTERFACE_MODE_MII;
else
bp->phy_interface = interface;
@@ -5213,6 +5490,12 @@ static int __maybe_unused macb_runtime_suspend(struct device *dev)
struct net_device *netdev = dev_get_drvdata(dev);
struct macb *bp = netdev_priv(netdev);
+ /*On ACPI platforms, clocks are controlled by firmware and/or
+ * ACPI, not by drivers.
+ */
+ if (unlikely(macb_has_acpi(bp)))
+ return 0;
+
if (!(device_may_wakeup(dev)))
macb_clks_disable(bp->pclk, bp->hclk, bp->tx_clk, bp->rx_clk, bp->tsu_clk);
else if (!(bp->caps & MACB_CAPS_NEED_TSUCLK))
@@ -5226,6 +5509,12 @@ static int __maybe_unused macb_runtime_resume(struct device *dev)
struct net_device *netdev = dev_get_drvdata(dev);
struct macb *bp = netdev_priv(netdev);
+ /*On ACPI platforms, clocks are controlled by firmware and/or
+ * ACPI, not by drivers.
+ */
+ if (unlikely(macb_has_acpi(bp)))
+ return 0;
+
if (!(device_may_wakeup(dev))) {
clk_prepare_enable(bp->pclk);
clk_prepare_enable(bp->hclk);
@@ -5250,7 +5539,10 @@ static struct platform_driver macb_driver = {
.driver = {
.name = "macb",
.of_match_table = of_match_ptr(macb_dt_ids),
- .pm = &macb_pm_ops,
+#ifdef CONFIG_ACPI
+ .acpi_match_table = ACPI_PTR(macb_acpi_ids),
+#endif
+ .pm = &macb_pm_ops,
},
};
--
2.17.1