// SPDX-License-Identifier: GPL-2.0-only /******************************************************************************* This contains the functions to handle the platform driver.
Copyright (C) 2007-2011 STMicroelectronics Ltd
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
/** * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins * @dev: struct device of the platform device * @mcast_bins: Multicast filtering bins * Description: * this function validates the number of Multicast filtering bins specified * by the configuration through the device tree. The Synopsys GMAC supports * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is * invalid and will cause the filtering algorithm to use Multicast * promiscuous mode.
*/ staticint dwmac1000_validate_mcast_bins(struct device *dev, int mcast_bins)
{ int x = mcast_bins;
switch (x) { case HASH_TABLE_SIZE: case 128: case 256: break; default:
x = 0;
dev_info(dev, "Hash table entries set to unexpected value %d\n",
mcast_bins); break;
} return x;
}
/** * dwmac1000_validate_ucast_entries - validate the Unicast address entries * @dev: struct device of the platform device * @ucast_entries: number of Unicast address entries * Description: * This function validates the number of Unicast address entries supported * by a particular Synopsys 10/100/1000 controller. The Synopsys controller * supports 1..32, 64, or 128 Unicast filter entries for its Unicast filter * logic. This function validates a valid, supported configuration is * selected, and defaults to 1 Unicast address if an unsupported * configuration is selected.
*/ staticint dwmac1000_validate_ucast_entries(struct device *dev, int ucast_entries)
{ int x = ucast_entries;
switch (x) { case 1 ... 32: case 64: case 128: break; default:
x = 1;
dev_info(dev, "Unicast table entries set to unexpected value %d\n",
ucast_entries); break;
} return x;
}
/** * stmmac_axi_setup - parse DT parameters for programming the AXI register * @pdev: platform device * Description: * if required, from device-tree the AXI internal register can be tuned * by using platform parameters.
*/ staticstruct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
{ struct device_node *np; struct stmmac_axi *axi;
np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0); if (!np) return NULL;
/* For backwards-compatibility with device trees that don't have any * snps,mtl-rx-config or snps,mtl-tx-config properties, we fall back * to one RX and TX queues each.
*/
plat->rx_queues_to_use = 1;
plat->tx_queues_to_use = 1;
/* First Queue must always be in DCB mode. As MTL_QUEUE_DCB = 1 we need * to always set this, otherwise Queue will be classified as AVB * (because MTL_QUEUE_AVB = 0).
*/
plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB;
plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB;
rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0); if (!rx_node) return ret;
if (of_property_read_u32(q_node, "snps,map-to-dma-channel",
&plat->rx_queues_cfg[queue].chan))
plat->rx_queues_cfg[queue].chan = queue; /* TODO: Dynamic mapping to be included in the future */
/** * stmmac_of_get_mdio() - Gets the MDIO bus from the devicetree. * @np: devicetree node * * The MDIO bus will be searched for in the following ways: * 1. The compatible is "snps,dwc-qos-ethernet-4.10" && a "mdio" named * child node exists * 2. A child node with the "snps,dwmac-mdio" compatible is present * * Return: The MDIO node if present otherwise NULL
*/ staticstruct device_node *stmmac_of_get_mdio(struct device_node *np)
{ staticconststruct of_device_id need_mdio_ids[] = {
{ .compatible = "snps,dwc-qos-ethernet-4.10" },
{},
}; struct device_node *mdio_node = NULL;
if (of_match_node(need_mdio_ids, np)) {
mdio_node = of_get_child_by_name(np, "mdio");
} else { /** * If snps,dwmac-mdio is passed from DT, always register * the MDIO
*/
for_each_child_of_node(np, mdio_node) { if (of_device_is_compatible(mdio_node, "snps,dwmac-mdio")) break;
}
}
return mdio_node;
}
/** * stmmac_mdio_setup() - Populate platform related MDIO structures. * @plat: driver data platform structure * @np: devicetree node * @dev: device pointer * * This searches for MDIO information from the devicetree. * If an MDIO node is found, it's assigned to plat->mdio_node and * plat->mdio_bus_data is allocated. * If no connection can be determined, just plat->mdio_bus_data is allocated * to indicate a bus should be created and scanned for a phy. * If it's determined there's no MDIO bus needed, both are left NULL. * * This expects that plat->phy_node has already been searched for. * * Return: 0 on success, errno otherwise.
*/ staticint stmmac_mdio_setup(struct plat_stmmacenet_data *plat, struct device_node *np, struct device *dev)
{ bool legacy_mdio;
plat->mdio_node = stmmac_of_get_mdio(np); if (plat->mdio_node)
dev_dbg(dev, "Found MDIO subnode\n");
/* Legacy devicetrees allowed for no MDIO bus description and expect * the bus to be scanned for devices. If there's no phy or fixed-link * described assume this is the case since there must be something * connected to the MAC.
*/
legacy_mdio = !of_phy_is_fixed_link(np) && !plat->phy_node; if (legacy_mdio)
dev_info(dev, "Deprecated MDIO bus assumption used\n");
if (plat->mdio_node || legacy_mdio) {
plat->mdio_bus_data = devm_kzalloc(dev, sizeof(*plat->mdio_bus_data),
GFP_KERNEL); if (!plat->mdio_bus_data) return -ENOMEM;
plat->mdio_bus_data->needs_reset = true;
}
return 0;
}
/** * stmmac_of_get_mac_mode - retrieves the interface of the MAC * @np: - device-tree node * Description: * Similar to `of_get_phy_mode()`, this function will retrieve (from * the device-tree) the interface mode on the MAC side. This assumes * that there is mode converter in-between the MAC & PHY * (e.g. GMII-to-RGMII).
*/ staticint stmmac_of_get_mac_mode(struct device_node *np)
{ constchar *pm; int err, i;
/** * stmmac_probe_config_dt - parse device-tree driver parameters * @pdev: platform_device structure * @mac: MAC address to use * Description: * this function is to read the driver parameters from device-tree and * set some private fields that will be used by the main at runtime.
*/ staticstruct plat_stmmacenet_data *
stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
{ struct device_node *np = pdev->dev.of_node; struct plat_stmmacenet_data *plat; struct stmmac_dma_cfg *dma_cfg; staticint bus_id = -ENODEV; int phy_mode; void *ret; int rc;
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); if (!plat) return ERR_PTR(-ENOMEM);
rc = of_get_mac_address(np, mac); if (rc) { if (rc == -EPROBE_DEFER) return ERR_PTR(rc);
eth_zero_addr(mac);
}
phy_mode = device_get_phy_mode(&pdev->dev); if (phy_mode < 0) return ERR_PTR(phy_mode);
/* Some wrapper drivers still rely on phy_node. Let's save it while
* they are not converted to phylink. */
plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
/* Get max speed of operation from device tree */
of_property_read_u32(np, "max-speed", &plat->max_speed);
plat->bus_id = of_alias_get_id(np, "ethernet"); if (plat->bus_id < 0) { if (bus_id < 0)
bus_id = of_alias_get_highest_id("ethernet"); /* No ethernet alias found, init at -1 so first bus_id is 0 */ if (bus_id < 0)
bus_id = -1;
plat->bus_id = ++bus_id;
}
/* Default to phy auto-detection */
plat->phy_addr = -1;
/* Default to get clk_csr from stmmac_clk_csr_set(), * or get clk_csr from device tree.
*/
plat->clk_csr = -1; if (of_property_read_u32(np, "snps,clk-csr", &plat->clk_csr))
of_property_read_u32(np, "clk_csr", &plat->clk_csr);
/* "snps,phy-addr" is not a standard property. Mark it as deprecated * and warn of its use. Remove this when phy node support is added.
*/ if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
rc = stmmac_mdio_setup(plat, np, &pdev->dev); if (rc) {
ret = ERR_PTR(rc); goto error_put_phy;
}
if (of_property_read_bool(np, "snps,en-tx-lpi-clockgating")) {
dev_warn(&pdev->dev, "OF property snps,en-tx-lpi-clockgating is deprecated, please convert driver to use STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP\n");
plat->flags |= STMMAC_FLAG_EN_TX_LPI_CLOCKGATING;
}
/* Set the maxmtu to a default of JUMBO_LEN in case the * parameter is not present in the device tree.
*/
plat->maxmtu = JUMBO_LEN;
/* Set default value for multicast hash bins */
plat->multicast_filter_bins = HASH_TABLE_SIZE;
/* Set default value for unicast filter entries */
plat->unicast_filter_entries = 1;
/* * Currently only the properties needed on SPEAr600 * are provided. All other properties should be added * once needed on other platforms.
*/ if (of_device_is_compatible(np, "st,spear600-gmac") ||
of_device_is_compatible(np, "snps,dwmac-3.50a") ||
of_device_is_compatible(np, "snps,dwmac-3.70a") ||
of_device_is_compatible(np, "snps,dwmac-3.72a") ||
of_device_is_compatible(np, "snps,dwmac")) { /* Note that the max-frame-size parameter as defined in the * ePAPR v1.1 spec is defined as max-frame-size, it's * actually used as the IEEE definition of MAC Client * data, or MTU. The ePAPR specification is confusing as * the definition is max-frame-size, but usage examples * are clearly MTUs
*/
of_property_read_u32(np, "max-frame-size", &plat->maxmtu);
of_property_read_u32(np, "snps,multicast-filter-bins",
&plat->multicast_filter_bins);
of_property_read_u32(np, "snps,perfect-filter-entries",
&plat->unicast_filter_entries);
plat->unicast_filter_entries = dwmac1000_validate_ucast_entries(
&pdev->dev, plat->unicast_filter_entries);
plat->multicast_filter_bins = dwmac1000_validate_mcast_bins(
&pdev->dev, plat->multicast_filter_bins);
plat->has_gmac = 1;
plat->pmt = 1;
}
rc = stmmac_mtl_setup(pdev, plat); if (rc) {
ret = ERR_PTR(rc); goto error_put_mdio;
}
/* clock setup */ if (!of_device_is_compatible(np, "snps,dwc-qos-ethernet-4.10")) {
plat->stmmac_clk = devm_clk_get(&pdev->dev,
STMMAC_RESOURCE_NAME); if (IS_ERR(plat->stmmac_clk)) {
dev_warn(&pdev->dev, "Cannot get CSR clock\n");
plat->stmmac_clk = NULL;
}
clk_prepare_enable(plat->stmmac_clk);
}
plat->pclk = devm_clk_get_optional(&pdev->dev, "pclk"); if (IS_ERR(plat->pclk)) {
ret = plat->pclk; goto error_pclk_get;
}
clk_prepare_enable(plat->pclk);
/* Fall-back to main clock in case of no PTP ref is passed */
plat->clk_ptp_ref = devm_clk_get(&pdev->dev, "ptp_ref"); if (IS_ERR(plat->clk_ptp_ref)) {
plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk);
plat->clk_ptp_ref = NULL;
dev_info(&pdev->dev, "PTP uses main clock\n");
} else {
plat->clk_ptp_rate = clk_get_rate(plat->clk_ptp_ref);
dev_dbg(&pdev->dev, "PTP rate %lu\n", plat->clk_ptp_rate);
}
plat->stmmac_rst = devm_reset_control_get_optional(&pdev->dev,
STMMAC_RESOURCE_NAME); if (IS_ERR(plat->stmmac_rst)) {
ret = plat->stmmac_rst; goto error_hw_init;
}
plat->stmmac_ahb_rst = devm_reset_control_get_optional_shared(
&pdev->dev, "ahb"); if (IS_ERR(plat->stmmac_ahb_rst)) {
ret = plat->stmmac_ahb_rst; goto error_hw_init;
}
/* Get IRQ information early to have an ability to ask for deferred * probe if needed before we went too far with resource allocation.
*/
stmmac_res->irq = platform_get_irq_byname(pdev, "macirq"); if (stmmac_res->irq < 0) return stmmac_res->irq;
/* On some platforms e.g. SPEAr the wake up irq differs from the mac irq * The external wake up irq can be passed through the platform code * named as "eth_wake_irq" * * In case the wake up interrupt is not passed from the platform * so the driver will continue to use the mac irq (ndev->irq)
*/
stmmac_res->wol_irq =
platform_get_irq_byname_optional(pdev, "eth_wake_irq"); if (stmmac_res->wol_irq < 0) { if (stmmac_res->wol_irq == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_info(&pdev->dev, "IRQ eth_wake_irq not found\n");
stmmac_res->wol_irq = stmmac_res->irq;
}
stmmac_res->lpi_irq =
platform_get_irq_byname_optional(pdev, "eth_lpi"); if (stmmac_res->lpi_irq < 0) { if (stmmac_res->lpi_irq == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_info(&pdev->dev, "IRQ eth_lpi not found\n");
}
stmmac_res->sfty_irq =
platform_get_irq_byname_optional(pdev, "sfty"); if (stmmac_res->sfty_irq < 0) { if (stmmac_res->sfty_irq == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_info(&pdev->dev, "IRQ sfty not found\n");
}
/** * stmmac_pltfr_suspend * @dev: device pointer * Description: this function is invoked when suspend the driver and it direcly * call the main suspend function and then, if required, on some platform, it * can call an exit helper.
*/ staticint __maybe_unused stmmac_pltfr_suspend(struct device *dev)
{ int ret; struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); struct platform_device *pdev = to_platform_device(dev);
ret = stmmac_suspend(dev);
stmmac_pltfr_exit(pdev, priv->plat);
return ret;
}
/** * stmmac_pltfr_resume * @dev: device pointer * Description: this function is invoked when resume the driver before calling * the main resume function, on some platforms, it can call own init helper * if required.
*/ staticint __maybe_unused stmmac_pltfr_resume(struct device *dev)
{ struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); struct platform_device *pdev = to_platform_device(dev); int ret;
ret = stmmac_pltfr_init(pdev, priv->plat); if (ret) return ret;
if (!device_may_wakeup(priv->device) || !priv->plat->pmt) { /* enable the clk previously disabled */
ret = pm_runtime_force_resume(dev); if (ret) return ret;
ret = clk_prepare_enable(priv->plat->clk_ptp_ref); if (ret < 0) {
netdev_warn(priv->dev, "failed to enable PTP reference clock: %pe\n",
ERR_PTR(ret)); return ret;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.