diff options
Diffstat (limited to 'drivers/net/dwc_eth_xgmac_socfpga.c')
-rw-r--r-- | drivers/net/dwc_eth_xgmac_socfpga.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/drivers/net/dwc_eth_xgmac_socfpga.c b/drivers/net/dwc_eth_xgmac_socfpga.c new file mode 100644 index 0000000..270c1b0 --- /dev/null +++ b/drivers/net/dwc_eth_xgmac_socfpga.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Intel Corporation + */ +#include <clk.h> +#include <cpu_func.h> +#include <dm.h> +#include <errno.h> +#include <eth_phy.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <miiphy.h> +#include <net.h> +#include <netdev.h> +#include <phy.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/arch/secure_reg_helper.h> +#include <asm/arch/system_manager.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/cache.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <dm/device_compat.h> +#include "dwc_eth_xgmac.h" + +#define SOCFPGA_XGMAC_SYSCON_ARG_COUNT 2 + +static int dwxgmac_socfpga_do_setphy(struct udevice *dev, u32 modereg) +{ + struct xgmac_priv *xgmac = dev_get_priv(dev); + int ret; + + u32 modemask = SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << + xgmac->syscon_phy_regshift; + + if (!(IS_ENABLED(CONFIG_SPL_BUILD)) && IS_ENABLED(CONFIG_SPL_ATF)) { + u32 index = ((u64)xgmac->syscon_phy - socfpga_get_sysmgr_addr() - + SYSMGR_SOC64_EMAC0) >> 2; + + u32 id = SOCFPGA_SECURE_REG_SYSMGR_SOC64_EMAC0 + index; + + ret = socfpga_secure_reg_update32(id, + modemask, + modereg << + xgmac->syscon_phy_regshift); + if (ret) { + dev_err(dev, "Failed to set PHY register via SMC call\n"); + return ret; + } + + } else { + clrsetbits_le32(xgmac->phy, modemask, modereg); + } + + return 0; +} + +static int xgmac_probe_resources_socfpga(struct udevice *dev) +{ + struct xgmac_priv *xgmac = dev_get_priv(dev); + struct regmap *reg_map; + struct ofnode_phandle_args args; + void *range; + phy_interface_t interface; + int ret; + u32 modereg; + + interface = xgmac->config->interface(dev); + + switch (interface) { + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_GMII: + modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; + break; + case PHY_INTERFACE_MODE_RMII: + modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; + break; + default: + dev_err(dev, "Unsupported PHY mode\n"); + return -EINVAL; + } + + /* Get PHY syscon */ + ret = dev_read_phandle_with_args(dev, "altr,sysmgr-syscon", NULL, + SOCFPGA_XGMAC_SYSCON_ARG_COUNT, + 0, &args); + + if (ret) { + dev_err(dev, "Failed to get syscon: %d\n", ret); + return ret; + } + + if (args.args_count != SOCFPGA_XGMAC_SYSCON_ARG_COUNT) { + dev_err(dev, "Invalid number of syscon args\n"); + return -EINVAL; + } + + reg_map = syscon_node_to_regmap(args.node); + if (IS_ERR(reg_map)) { + ret = PTR_ERR(reg_map); + dev_err(dev, "Failed to get reg_map: %d\n", ret); + return ret; + } + + range = regmap_get_range(reg_map, 0); + if (!range) { + dev_err(dev, "Failed to get reg_map: %d\n", ret); + return -ENOMEM; + } + + xgmac->syscon_phy = range + args.args[0]; + xgmac->syscon_phy_regshift = args.args[1]; + + /* Get Reset Bulk */ + ret = reset_get_bulk(dev, &xgmac->reset_bulk); + if (ret) { + dev_err(dev, "Failed to get reset: %d\n", ret); + return ret; + } + + ret = reset_assert_bulk(&xgmac->reset_bulk); + if (ret) { + dev_err(dev, "XGMAC failed to assert reset: %d\n", ret); + return ret; + } + + ret = dwxgmac_socfpga_do_setphy(dev, modereg); + if (ret) + return ret; + + ret = reset_deassert_bulk(&xgmac->reset_bulk); + if (ret) { + dev_err(dev, "XGMAC failed to de-assert reset: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "stmmaceth", &xgmac->clk_common); + if (ret) { + pr_err("clk_get_by_name(stmmaceth) failed: %d", ret); + goto err_probe; + } + return 0; + +err_probe: + debug("%s: returns %d\n", __func__, ret); + return ret; +} + +static int xgmac_get_enetaddr_socfpga(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct xgmac_priv *xgmac = dev_get_priv(dev); + u32 hi_addr, lo_addr; + + debug("%s(dev=%p):\n", __func__, dev); + + /* Read the MAC Address from the hardawre */ + hi_addr = readl(&xgmac->mac_regs->address0_high); + lo_addr = readl(&xgmac->mac_regs->address0_low); + + pdata->enetaddr[0] = lo_addr & 0xff; + pdata->enetaddr[1] = (lo_addr >> 8) & 0xff; + pdata->enetaddr[2] = (lo_addr >> 16) & 0xff; + pdata->enetaddr[3] = (lo_addr >> 24) & 0xff; + pdata->enetaddr[4] = hi_addr & 0xff; + pdata->enetaddr[5] = (hi_addr >> 8) & 0xff; + + return !is_valid_ethaddr(pdata->enetaddr); +} + +static int xgmac_start_resets_socfpga(struct udevice *dev) +{ + struct xgmac_priv *xgmac = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + ret = reset_assert_bulk(&xgmac->reset_bulk); + if (ret < 0) { + pr_err("xgmac reset assert failed: %d", ret); + return ret; + } + + udelay(2); + + ret = reset_deassert_bulk(&xgmac->reset_bulk); + if (ret < 0) { + pr_err("xgmac reset de-assert failed: %d", ret); + return ret; + } + + return 0; +} + +static struct xgmac_ops xgmac_socfpga_ops = { + .xgmac_inval_desc = xgmac_inval_desc_generic, + .xgmac_flush_desc = xgmac_flush_desc_generic, + .xgmac_inval_buffer = xgmac_inval_buffer_generic, + .xgmac_flush_buffer = xgmac_flush_buffer_generic, + .xgmac_probe_resources = xgmac_probe_resources_socfpga, + .xgmac_remove_resources = xgmac_null_ops, + .xgmac_stop_resets = xgmac_null_ops, + .xgmac_start_resets = xgmac_start_resets_socfpga, + .xgmac_stop_clks = xgmac_null_ops, + .xgmac_start_clks = xgmac_null_ops, + .xgmac_calibrate_pads = xgmac_null_ops, + .xgmac_disable_calibration = xgmac_null_ops, + .xgmac_get_enetaddr = xgmac_get_enetaddr_socfpga, +}; + +struct xgmac_config __maybe_unused xgmac_socfpga_config = { + .reg_access_always_ok = false, + .swr_wait = 50, + .config_mac = XGMAC_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, + .config_mac_mdio = XGMAC_MAC_MDIO_ADDRESS_CR_350_400, + .axi_bus_width = XGMAC_AXI_WIDTH_64, + .interface = dev_read_phy_mode, + .ops = &xgmac_socfpga_ops +}; |