summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/fman/mac.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/freescale/fman/mac.c')
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
new file mode 100644
index 000000000000..f27ff625fe29
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/libfdt_env.h>
+#include <linux/platform_device.h>
+
+#include "mac.h"
+#include "fman_mac.h"
+#include "fman_dtsec.h"
+#include "fman_tgec.h"
+#include "fman_memac.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL FMan MAC API based driver");
+
+struct mac_priv_s {
+ u8 cell_index;
+ struct fman *fman;
+ struct platform_device *eth_dev;
+ u16 speed;
+};
+
+struct mac_address {
+ u8 addr[ETH_ALEN];
+ struct list_head list;
+};
+
+static void mac_exception(struct mac_device *mac_dev,
+ enum fman_mac_exceptions ex)
+{
+ if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) {
+ /* don't flag RX FIFO after the first */
+ mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_10G_RX_FIFO_OVFL, false);
+ dev_err(mac_dev->dev, "10G MAC got RX FIFO Error = %x\n", ex);
+ }
+
+ dev_dbg(mac_dev->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c",
+ __func__, ex);
+}
+
+static DEFINE_MUTEX(eth_lock);
+
+static struct platform_device *dpaa_eth_add_device(int fman_id,
+ struct mac_device *mac_dev)
+{
+ struct platform_device *pdev;
+ struct dpaa_eth_data data;
+ struct mac_priv_s *priv;
+ static int dpaa_eth_dev_cnt;
+ int ret;
+
+ priv = mac_dev->priv;
+
+ data.mac_dev = mac_dev;
+ data.mac_hw_id = priv->cell_index;
+ data.fman_hw_id = fman_id;
+
+ mutex_lock(&eth_lock);
+ pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto no_mem;
+ }
+
+ pdev->dev.parent = mac_dev->dev;
+
+ ret = platform_device_add_data(pdev, &data, sizeof(data));
+ if (ret)
+ goto err;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto err;
+
+ dpaa_eth_dev_cnt++;
+ mutex_unlock(&eth_lock);
+
+ return pdev;
+
+err:
+ platform_device_put(pdev);
+no_mem:
+ mutex_unlock(&eth_lock);
+
+ return ERR_PTR(ret);
+}
+
+static const struct of_device_id mac_match[] = {
+ { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization },
+ { .compatible = "fsl,fman-xgec", .data = tgec_initialization },
+ { .compatible = "fsl,fman-memac", .data = memac_initialization },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mac_match);
+
+static int mac_probe(struct platform_device *_of_dev)
+{
+ int err, i, nph;
+ int (*init)(struct mac_device *mac_dev, struct device_node *mac_node,
+ struct fman_mac_params *params);
+ struct device *dev;
+ struct device_node *mac_node, *dev_node;
+ struct mac_device *mac_dev;
+ struct platform_device *of_dev;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+ u32 val;
+ u8 fman_id;
+ phy_interface_t phy_if;
+
+ dev = &_of_dev->dev;
+ mac_node = dev->of_node;
+ init = of_device_get_match_data(dev);
+
+ mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL);
+ if (!mac_dev)
+ return -ENOMEM;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(_of_dev, mac_dev);
+
+ /* Save private information */
+ mac_dev->priv = priv;
+ mac_dev->dev = dev;
+
+ /* Get the FM node */
+ dev_node = of_get_parent(mac_node);
+ if (!dev_node) {
+ dev_err(dev, "of_get_parent(%pOF) failed\n",
+ mac_node);
+ return -EINVAL;
+ }
+
+ of_dev = of_find_device_by_node(dev_node);
+ if (!of_dev) {
+ dev_err(dev, "of_find_device_by_node(%pOF) failed\n", dev_node);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+ mac_dev->fman_dev = &of_dev->dev;
+
+ /* Get the FMan cell-index */
+ err = of_property_read_u32(dev_node, "cell-index", &val);
+ if (err) {
+ dev_err(dev, "failed to read cell-index for %pOF\n", dev_node);
+ err = -EINVAL;
+ goto _return_dev_put;
+ }
+ /* cell-index 0 => FMan id 1 */
+ fman_id = (u8)(val + 1);
+
+ priv->fman = fman_bind(mac_dev->fman_dev);
+ if (!priv->fman) {
+ dev_err(dev, "fman_bind(%pOF) failed\n", dev_node);
+ err = -ENODEV;
+ goto _return_dev_put;
+ }
+
+ /* Two references have been taken in of_find_device_by_node()
+ * and fman_bind(). Release one of them here. The second one
+ * will be released in mac_remove().
+ */
+ put_device(mac_dev->fman_dev);
+ of_node_put(dev_node);
+ dev_node = NULL;
+
+ /* Get the address of the memory mapped registers */
+ mac_dev->res = platform_get_mem_or_io(_of_dev, 0);
+ if (!mac_dev->res) {
+ dev_err(dev, "could not get registers\n");
+ err = -EINVAL;
+ goto _return_dev_put;
+ }
+
+ err = devm_request_resource(dev, fman_get_mem_region(priv->fman),
+ mac_dev->res);
+ if (err) {
+ dev_err_probe(dev, err, "could not request resource\n");
+ goto _return_dev_put;
+ }
+
+ mac_dev->vaddr = devm_ioremap(dev, mac_dev->res->start,
+ resource_size(mac_dev->res));
+ if (!mac_dev->vaddr) {
+ dev_err(dev, "devm_ioremap() failed\n");
+ err = -EIO;
+ goto _return_dev_put;
+ }
+
+ if (!of_device_is_available(mac_node)) {
+ err = -ENODEV;
+ goto _return_dev_put;
+ }
+
+ /* Get the cell-index */
+ err = of_property_read_u32(mac_node, "cell-index", &val);
+ if (err) {
+ dev_err(dev, "failed to read cell-index for %pOF\n", mac_node);
+ err = -EINVAL;
+ goto _return_dev_put;
+ }
+ if (val >= MAX_NUM_OF_MACS) {
+ dev_err(dev, "cell-index value is too big for %pOF\n", mac_node);
+ err = -EINVAL;
+ goto _return_dev_put;
+ }
+ priv->cell_index = (u8)val;
+
+ /* Get the MAC address */
+ err = of_get_mac_address(mac_node, mac_dev->addr);
+ if (err)
+ dev_warn(dev, "of_get_mac_address(%pOF) failed\n", mac_node);
+
+ /* Get the port handles */
+ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL);
+ if (unlikely(nph < 0)) {
+ dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n",
+ mac_node);
+ err = nph;
+ goto _return_dev_put;
+ }
+
+ if (nph != ARRAY_SIZE(mac_dev->port)) {
+ dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n",
+ mac_node);
+ err = -EINVAL;
+ goto _return_dev_put;
+ }
+
+ /* PORT_NUM determines the size of the port array */
+ for (i = 0; i < PORT_NUM; i++) {
+ /* Find the port node */
+ dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i);
+ if (!dev_node) {
+ dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n",
+ mac_node);
+ err = -EINVAL;
+ goto _return_dev_arr_put;
+ }
+
+ of_dev = of_find_device_by_node(dev_node);
+ if (!of_dev) {
+ dev_err(dev, "of_find_device_by_node(%pOF) failed\n",
+ dev_node);
+ err = -EINVAL;
+ goto _return_dev_arr_put;
+ }
+ mac_dev->fman_port_devs[i] = &of_dev->dev;
+
+ mac_dev->port[i] = fman_port_bind(mac_dev->fman_port_devs[i]);
+ if (!mac_dev->port[i]) {
+ dev_err(dev, "dev_get_drvdata(%pOF) failed\n",
+ dev_node);
+ err = -EINVAL;
+ goto _return_dev_arr_put;
+ }
+ /* Two references have been taken in of_find_device_by_node()
+ * and fman_port_bind(). Release one of them here. The second
+ * one will be released in mac_remove().
+ */
+ put_device(mac_dev->fman_port_devs[i]);
+ of_node_put(dev_node);
+ dev_node = NULL;
+ }
+
+ /* Get the PHY connection type */
+ err = of_get_phy_mode(mac_node, &phy_if);
+ if (err) {
+ dev_warn(dev,
+ "of_get_phy_mode() for %pOF failed. Defaulting to SGMII\n",
+ mac_node);
+ phy_if = PHY_INTERFACE_MODE_SGMII;
+ }
+ mac_dev->phy_if = phy_if;
+
+ params.mac_id = priv->cell_index;
+ params.fm = (void *)priv->fman;
+ params.exception_cb = mac_exception;
+ params.event_cb = mac_exception;
+
+ err = init(mac_dev, mac_node, &params);
+ if (err < 0)
+ goto _return_dev_arr_put;
+
+ if (!is_zero_ether_addr(mac_dev->addr))
+ dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr);
+
+ priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev);
+ if (IS_ERR(priv->eth_dev)) {
+ err = PTR_ERR(priv->eth_dev);
+ dev_err(dev, "failed to add Ethernet platform device for MAC %d\n",
+ priv->cell_index);
+ priv->eth_dev = NULL;
+ }
+
+ return err;
+
+_return_dev_arr_put:
+ /* mac_dev is kzalloc'ed */
+ for (i = 0; i < PORT_NUM; i++)
+ put_device(mac_dev->fman_port_devs[i]);
+_return_dev_put:
+ put_device(mac_dev->fman_dev);
+_return_of_node_put:
+ of_node_put(dev_node);
+ return err;
+}
+
+static void mac_remove(struct platform_device *pdev)
+{
+ struct mac_device *mac_dev = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < PORT_NUM; i++)
+ put_device(mac_dev->fman_port_devs[i]);
+ put_device(mac_dev->fman_dev);
+
+ platform_device_unregister(mac_dev->priv->eth_dev);
+}
+
+static struct platform_driver mac_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = mac_match,
+ },
+ .probe = mac_probe,
+ .remove = mac_remove,
+};
+
+builtin_platform_driver(mac_driver);