dpaa2-mac: probe standalone DPMAC objects
authorIoana Ciornei <ioana.ciornei@nxp.com>
Tue, 22 Sep 2020 13:19:07 +0000 (16:19 +0300)
committerIoana Ciornei <ciorneiioana@gmail.com>
Mon, 26 Apr 2021 09:22:47 +0000 (12:22 +0300)
In upstream, at the moment, we only support DPMAC objects which are
connected to DPNIs driver by the Linux kernel. To close the gap (aka
driver any other DPMAC object in TYPE_PHY) we build a standalone
driver around the dpaa2-mac.

Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
drivers/net/ethernet/freescale/dpaa2/Kconfig
drivers/net/ethernet/freescale/dpaa2/Makefile
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
drivers/net/ethernet/freescale/dpaa2/dpmac-cmd.h
drivers/net/ethernet/freescale/dpaa2/dpmac.c
drivers/net/ethernet/freescale/dpaa2/dpmac.h
drivers/net/ethernet/freescale/dpaa2/old-mac [new symlink]

index 3eb4277..c8b627d 100644 (file)
@@ -12,6 +12,27 @@ config FSL_DPAA2_ETH
          The driver manages network objects discovered on the Freescale
          MC bus.
 
+config FSL_DPAA2_MAC
+       tristate "Freescale DPAA2 MAC driver"
+       depends on FSL_MC_BUS
+       select FSL_XGMAC_MDIO
+       select PHYLINK
+       select PCS_LYNX
+       help
+         This is the DPAA2 MAC independent driver which aims to support dpmac
+         fsl_mc devices which are not connected to other objects found in the
+         root dprc.
+
+config FSL_DPAA2_MAC_NETDEVS
+       bool "Expose net interfaces for MAC devices"
+       default n
+       depends on FSL_DPAA2_MAC
+       help
+         Exposes macX net interfaces for MAC devices which are not connected to
+         a host managed DPNI. This allows direct control over those MACs and
+         their corresponding PHYs.
+         Leave disabled if unsure.
+
 if FSL_DPAA2_ETH
 config FSL_DPAA2_ETH_DCB
        bool "Data Center Bridging (DCB) Support"
index de3bd9f..cb6d069 100644 (file)
@@ -5,12 +5,14 @@
 
 obj-$(CONFIG_FSL_DPAA2_ETH)            += fsl-dpaa2-eth.o
 obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)      += fsl-dpaa2-ptp.o
+obj-$(CONFIG_FSL_DPAA2_MAC)            += fsl-dpaa2-mac.o
 
 fsl-dpaa2-eth-objs     := dpaa2-eth.o dpaa2-ethtool.o dpni.o dpaa2-mac.o dpmac.o dpaa2-eth-devlink.o
 fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DCB} += dpaa2-eth-dcb.o
 fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
 fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_CEETM} += dpaa2-eth-ceetm.o
 fsl-dpaa2-ptp-objs     := dpaa2-ptp.o dprtc.o
+fsl-dpaa2-mac-objs     := dpaa2-mac.o dpmac.o
 
 # Needed by the tracing framework
 CFLAGS_dpaa2-eth.o := -I$(src)
index f80ecee..37aca02 100644 (file)
 #include <linux/bpf_trace.h>
 #include <linux/fsl/ptp_qoriq.h>
 #include <linux/ptp_classify.h>
+#include <linux/device/driver.h>
 #include <net/pkt_cls.h>
 #include <net/sock.h>
 
+/* Hack: only here in order to use device_driver_detach.
+ * To be replaced with the use of MC _ENDPOINT_CHANGED interrupts on all
+ * connectable objects
+ */
+#include "../../../../base/base.h"
 #include "dpaa2-eth.h"
 #include "dpaa2-eth-ceetm.h"
 
@@ -4069,6 +4075,24 @@ static int dpaa2_eth_poll_link_state(void *arg)
        return 0;
 }
 
+static void dpaa2_eth_dpmac_driver_attach(struct fsl_mc_device *dpmac_dev)
+{
+       struct device_driver *drv = driver_find("fsl_dpaa2_mac", &fsl_mc_bus_type);
+       struct device *dev = &dpmac_dev->dev;
+
+       if (dev && dev->driver == NULL && driver_match_device(drv, dev))
+               device_driver_attach(drv, dev);
+}
+
+static void dpaa2_eth_dpmac_driver_detach(struct fsl_mc_device *dpmac_dev)
+{
+       struct device_driver *drv = driver_find("fsl_dpaa2_mac", &fsl_mc_bus_type);
+       struct device *dev = &dpmac_dev->dev;
+
+       if (dev && dev->driver == drv)
+               device_driver_detach(dev);
+}
+
 static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
 {
        struct fsl_mc_device *dpni_dev, *dpmac_dev;
@@ -4084,6 +4108,8 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
        if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type)
                return 0;
 
+       dpaa2_eth_dpmac_driver_detach(dpmac_dev);
+
        mac = kzalloc(sizeof(struct dpaa2_mac), GFP_KERNEL);
        if (!mac)
                return -ENOMEM;
@@ -4124,6 +4150,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
                return;
 
        dpaa2_mac_close(priv->mac);
+       dpaa2_eth_dpmac_driver_attach(priv->mac->mc_dev);
        kfree(priv->mac);
        priv->mac = NULL;
 }
index 0172cc3..65418be 100644 (file)
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /* Copyright 2019 NXP */
 
+#include <linux/fsl/mc.h>
+#include <linux/msi.h>
 #include "dpaa2-eth.h"
 #include "dpaa2-mac.h"
 
@@ -34,6 +36,16 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
        return 0;
 }
 
+static bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac)
+{
+       if (mac &&
+           (mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
+            mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
+               return true;
+
+       return false;
+}
+
 /* Caller must call of_node_put on the returned value */
 static struct device_node *dpaa2_mac_get_node(u16 dpmac_id)
 {
@@ -478,3 +490,409 @@ void dpaa2_mac_get_ethtool_stats(struct dpaa2_mac *mac, u64 *data)
                *(data + i) = value;
        }
 }
+
+struct dpaa2_mac_link_mode_map {
+       u64 dpmac_lm;
+       enum ethtool_link_mode_bit_indices ethtool_lm;
+};
+
+static const struct dpaa2_mac_link_mode_map dpaa2_mac_lm_map[] = {
+       {DPMAC_ADVERTISED_10BASET_FULL, ETHTOOL_LINK_MODE_10baseT_Full_BIT},
+       {DPMAC_ADVERTISED_100BASET_FULL, ETHTOOL_LINK_MODE_100baseT_Full_BIT},
+       {DPMAC_ADVERTISED_1000BASET_FULL, ETHTOOL_LINK_MODE_1000baseT_Full_BIT},
+       {DPMAC_ADVERTISED_10000BASET_FULL, ETHTOOL_LINK_MODE_10000baseT_Full_BIT},
+       {DPMAC_ADVERTISED_AUTONEG, ETHTOOL_LINK_MODE_Autoneg_BIT},
+};
+
+static void dpaa2_mac_ksettings_change(struct dpaa2_mac *priv)
+{
+       struct fsl_mc_device *mc_dev = priv->mc_dev;
+       struct dpmac_link_cfg link_cfg = { 0 };
+       int err, i;
+
+       err = dpmac_get_link_cfg(priv->mc_io, 0,
+                                mc_dev->mc_handle,
+                                &link_cfg);
+       if (err) {
+               dev_err(&mc_dev->dev, "dpmac_get_link_cfg() = %d\n", err);
+               return;
+       }
+
+       phylink_ethtool_ksettings_get(priv->phylink, &priv->kset);
+
+       priv->kset.base.speed = link_cfg.rate;
+       priv->kset.base.duplex = !!(link_cfg.options & DPMAC_LINK_OPT_HALF_DUPLEX);
+
+       ethtool_link_ksettings_zero_link_mode(&priv->kset, advertising);
+       for (i = 0; i < ARRAY_SIZE(dpaa2_mac_lm_map); i++) {
+               if (link_cfg.advertising & dpaa2_mac_lm_map[i].dpmac_lm)
+                       __set_bit(dpaa2_mac_lm_map[i].ethtool_lm,
+                                 priv->kset.link_modes.advertising);
+       }
+
+       if (link_cfg.options & DPMAC_LINK_OPT_AUTONEG) {
+               priv->kset.base.autoneg = AUTONEG_ENABLE;
+               __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                         priv->kset.link_modes.advertising);
+       } else {
+               priv->kset.base.autoneg = AUTONEG_DISABLE;
+               __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                           priv->kset.link_modes.advertising);
+       }
+
+       phylink_ethtool_ksettings_set(priv->phylink, &priv->kset);
+}
+
+static irqreturn_t dpaa2_mac_irq_handler(int irq_num, void *arg)
+{
+       struct device *dev = (struct device *)arg;
+       struct fsl_mc_device *dpmac_dev;
+       struct net_device *net_dev;
+       struct dpaa2_mac *priv;
+       u32 status = ~0;
+       int err;
+
+       dpmac_dev = to_fsl_mc_device(dev);
+       net_dev = dev_get_drvdata(dev);
+       priv = netdev_priv(net_dev);
+
+       err = dpmac_get_irq_status(priv->mc_io, 0, dpmac_dev->mc_handle,
+                                 DPMAC_IRQ_INDEX, &status);
+       if (err) {
+               netdev_err(net_dev, "dpmac_get_irq_status() = %d\n", err);
+               return IRQ_HANDLED;
+       }
+
+       rtnl_lock();
+       if (status & DPMAC_IRQ_EVENT_LINK_CFG_REQ)
+               dpaa2_mac_ksettings_change(priv);
+
+       if (status & DPMAC_IRQ_EVENT_LINK_DOWN_REQ)
+               phylink_stop(priv->phylink);
+
+       if (status & DPMAC_IRQ_EVENT_LINK_UP_REQ)
+               phylink_start(priv->phylink);
+       rtnl_unlock();
+
+       dpmac_clear_irq_status(priv->mc_io, 0, dpmac_dev->mc_handle,
+                              DPMAC_IRQ_INDEX, status);
+
+       return IRQ_HANDLED;
+}
+
+static int dpaa2_mac_setup_irqs(struct fsl_mc_device *mc_dev)
+{
+       struct fsl_mc_device_irq *irq;
+       int err = 0;
+
+       err = fsl_mc_allocate_irqs(mc_dev);
+       if (err) {
+               dev_err(&mc_dev->dev, "fsl_mc_allocate_irqs err %d\n", err);
+               return err;
+       }
+
+       irq = mc_dev->irqs[0];
+       err = devm_request_threaded_irq(&mc_dev->dev, irq->msi_desc->irq,
+                                       NULL, &dpaa2_mac_irq_handler,
+                                       IRQF_NO_SUSPEND | IRQF_ONESHOT,
+                                       dev_name(&mc_dev->dev), &mc_dev->dev);
+       if (err) {
+               dev_err(&mc_dev->dev, "devm_request_threaded_irq err %d\n",
+                       err);
+               goto free_irq;
+       }
+
+       err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle,
+                                DPMAC_IRQ_INDEX, DPMAC_IRQ_EVENT_LINK_CFG_REQ |
+                                DPMAC_IRQ_EVENT_LINK_UP_REQ |
+                                DPMAC_IRQ_EVENT_LINK_DOWN_REQ);
+       if (err) {
+               dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err);
+               goto free_irq;
+       }
+       err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle,
+                                  DPMAC_IRQ_INDEX, 1);
+       if (err) {
+               dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err);
+               goto free_irq;
+       }
+
+       return 0;
+
+free_irq:
+       fsl_mc_free_irqs(mc_dev);
+
+       return err;
+}
+
+static void dpaa2_mac_teardown_irqs(struct fsl_mc_device *mc_dev)
+{
+       int err;
+
+       err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle,
+                                  DPMAC_IRQ_INDEX, 0);
+       if (err)
+               dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err);
+
+       fsl_mc_free_irqs(mc_dev);
+}
+
+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
+
+static int dpaa2_mac_netdev_open(struct net_device *net_dev)
+{
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       if (dpaa2_mac_is_type_phy(priv))
+               phylink_start(priv->phylink);
+
+       return 0;
+}
+
+static int dpaa2_mac_netdev_stop(struct net_device *net_dev)
+{
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       if (!dpaa2_mac_is_type_phy(priv))
+               return 0;
+
+       phylink_stop(priv->phylink);
+
+       return 0;
+}
+
+static netdev_tx_t dpaa2_mac_drop_frame(struct sk_buff *skb,
+                                       struct net_device *net_dev)
+{
+       /* These interfaces don't support I/O, they are only
+        * for control and debu
+        */
+       dev_kfree_skb_any(skb);
+       return NETDEV_TX_OK;
+}
+
+static void dpaa2_mac_get_drvinfo(struct net_device *net_dev,
+                                 struct ethtool_drvinfo *drvinfo)
+{
+       strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent),
+               sizeof(drvinfo->bus_info));
+}
+
+static int dpaa2_mac_get_link_ksettings(struct net_device *net_dev,
+                                       struct ethtool_link_ksettings *ks)
+{
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       if (dpaa2_mac_is_type_phy(priv))
+               return phylink_ethtool_ksettings_get(priv->phylink, ks);
+
+       return -EOPNOTSUPP;
+}
+
+static int dpaa2_mac_set_link_ksettings(struct net_device *net_dev,
+                                       const struct ethtool_link_ksettings *ks)
+{
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       if (!dpaa2_mac_is_type_phy(priv))
+               return -EOPNOTSUPP;
+
+       return phylink_ethtool_ksettings_set(priv->phylink, ks);
+}
+
+static int dpaa2_mac_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
+{
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       if (!dpaa2_mac_is_type_phy(priv))
+               return -EOPNOTSUPP;
+
+       return phylink_mii_ioctl(priv->phylink, rq, cmd);
+}
+
+static void dpaa2_mac_ethtool_get_strings(struct net_device *net_dev,
+                                         u32 stringset, u8 *data)
+{
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       dpaa2_mac_get_strings(data);
+}
+
+static void dpaa2_mac_ethtool_get_stats(struct net_device *net_dev,
+                                       struct ethtool_stats *stats,
+                                       u64 *data)
+{
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       dpaa2_mac_get_ethtool_stats(priv, data);
+}
+
+static int dpaa2_mac_ethtool_get_sset_count(struct net_device *dev, int sset)
+{
+       if (sset != ETH_SS_STATS)
+               return -EOPNOTSUPP;
+
+       return dpaa2_mac_get_sset_count();
+}
+
+static const struct net_device_ops dpaa2_mac_ndo_ops = {
+       .ndo_open               = &dpaa2_mac_netdev_open,
+       .ndo_stop               = &dpaa2_mac_netdev_stop,
+       .ndo_start_xmit         = &dpaa2_mac_drop_frame,
+       .ndo_do_ioctl           = &dpaa2_mac_ioctl,
+};
+
+static const struct ethtool_ops dpaa2_mac_ethtool_ops = {
+       .get_drvinfo            = &dpaa2_mac_get_drvinfo,
+       .get_link_ksettings     = &dpaa2_mac_get_link_ksettings,
+       .set_link_ksettings     = &dpaa2_mac_set_link_ksettings,
+       .get_strings            = &dpaa2_mac_ethtool_get_strings,
+       .get_ethtool_stats      = &dpaa2_mac_ethtool_get_stats,
+       .get_sset_count         = &dpaa2_mac_ethtool_get_sset_count,
+};
+#endif
+
+static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev)
+{
+       struct device *dev = &mc_dev->dev;
+       struct fsl_mc_device *peer_dev;
+       struct net_device *net_dev;
+       struct dpaa2_mac *priv;
+       int err;
+
+       /* If the DPMAC is connected to a DPNI from the currect DPRC, then
+        * there is no need for this standalone MAC driver, the dpaa2-eth will
+        * take care of anything related to MAC/PHY
+        */
+       peer_dev = fsl_mc_get_endpoint(mc_dev);
+       if (!IS_ERR_OR_NULL(peer_dev) && peer_dev->dev.type == &fsl_mc_bus_dpni_type)
+               return -EPROBE_DEFER;
+
+       net_dev = alloc_etherdev(sizeof(*priv));
+       if (!net_dev) {
+               dev_err(dev, "alloc_etherdev error\n");
+               return -ENOMEM;
+       }
+
+       priv = netdev_priv(net_dev);
+       priv->mc_dev = mc_dev;
+       priv->net_dev = net_dev;
+
+       SET_NETDEV_DEV(net_dev, dev);
+
+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
+       snprintf(net_dev->name, IFNAMSIZ, "mac%d", mc_dev->obj_desc.id);
+
+       /* register netdev just to make it visible to the user */
+       net_dev->netdev_ops = &dpaa2_mac_ndo_ops;
+       net_dev->ethtool_ops = &dpaa2_mac_ethtool_ops;
+
+       err = register_netdev(net_dev);
+       if (err) {
+               dev_err(dev, "register_netdev error %d\n", err);
+               goto free_netdev;
+       }
+#endif
+
+       dev_set_drvdata(dev, net_dev);
+
+       err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io);
+       if (err) {
+               if (err == -ENXIO)
+                       err = -EPROBE_DEFER;
+               else
+                       dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
+               goto unregister_netdev;
+       }
+       priv->mc_io = mc_dev->mc_io;
+
+       err = dpmac_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
+                        &mc_dev->mc_handle);
+       if (err || !mc_dev->mc_handle) {
+               dev_err(dev, "dpmac_open error: %d\n", err);
+               err = -ENODEV;
+               goto free_portal;
+       }
+
+       err = dpmac_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, &priv->attr);
+       if (err) {
+               dev_err(dev, "dpmac_get_attributes() = %d\n", err);
+               goto free_portal;
+       }
+
+       err = dpaa2_mac_setup_irqs(mc_dev);
+       if (err) {
+               err = -EFAULT;
+               goto free_portal;
+       }
+
+       err = dpaa2_mac_open(priv);
+       if (err)
+               goto free_portal;
+
+       if (dpaa2_mac_is_type_phy(priv)) {
+               err = dpaa2_mac_connect(priv);
+               if (err) {
+                       dev_err(dev, "Error connecting to the MAC endpoint\n");
+                       goto free_portal;
+               }
+       }
+
+       return 0;
+free_portal:
+       fsl_mc_portal_free(mc_dev->mc_io);
+unregister_netdev:
+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
+       unregister_netdev(net_dev);
+#endif
+free_netdev:
+       free_netdev(net_dev);
+
+       return err;
+}
+
+static int dpaa2_mac_remove(struct fsl_mc_device *mc_dev)
+{
+       struct device *dev = &mc_dev->dev;
+       struct net_device *net_dev = dev_get_drvdata(dev);
+       struct dpaa2_mac *priv = netdev_priv(net_dev);
+
+       dpaa2_mac_teardown_irqs(mc_dev);
+
+       if (dpaa2_mac_is_type_phy(priv)) {
+               dpaa2_mac_close(priv);
+               dpaa2_mac_disconnect(priv);
+       }
+
+       fsl_mc_portal_free(mc_dev->mc_io);
+       dev_set_drvdata(dev, NULL);
+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
+       unregister_netdev(net_dev);
+#endif
+       free_netdev(net_dev);
+
+       return 0;
+}
+
+static const struct fsl_mc_device_id dpaa2_mac_match_id_table[] = {
+       {
+               .vendor = FSL_MC_VENDOR_FREESCALE,
+               .obj_type = "dpmac",
+       },
+       { .vendor = 0x0 }
+};
+MODULE_DEVICE_TABLE(fslmc, dpaa2_mac_match_id_table);
+
+static struct fsl_mc_driver dpaa2_mac_driver = {
+       .driver = {
+               .name = "fsl_dpaa2_mac",
+               .owner = THIS_MODULE,
+       },
+       .probe = dpaa2_mac_probe,
+       .remove = dpaa2_mac_remove,
+       .match_id_table = dpaa2_mac_match_id_table
+};
+
+module_fsl_mc_driver(dpaa2_mac_driver);
index 13d42dd..fa23dec 100644 (file)
@@ -21,6 +21,7 @@ struct dpaa2_mac {
 
        struct phylink_config phylink_config;
        struct phylink *phylink;
+       struct ethtool_link_ksettings kset;
        phy_interface_t if_mode;
        enum dpmac_link_type if_link_type;
        struct lynx_pcs *pcs;
index a24b20f..326b8b2 100644 (file)
 #define DPMAC_CMDID_OPEN               DPMAC_CMD(0x80c)
 
 #define DPMAC_CMDID_GET_ATTR           DPMAC_CMD(0x004)
+
+#define DPMAC_CMDID_SET_IRQ_ENABLE     DPMAC_CMD(0x012)
+#define DPMAC_CMDID_GET_IRQ_ENABLE     DPMAC_CMD(0x013)
+#define DPMAC_CMDID_SET_IRQ_MASK       DPMAC_CMD(0x014)
+#define DPMAC_CMDID_GET_IRQ_MASK       DPMAC_CMD(0x015)
+#define DPMAC_CMDID_GET_IRQ_STATUS     DPMAC_CMD(0x016)
+#define DPMAC_CMDID_CLEAR_IRQ_STATUS   DPMAC_CMD(0x017)
+
+#define DPMAC_CMDID_GET_LINK_CFG       DPMAC_CMD_V2(0x0c2)
 #define DPMAC_CMDID_SET_LINK_STATE     DPMAC_CMD_V2(0x0c3)
 
 #define DPMAC_CMDID_GET_COUNTER                DPMAC_CMD(0x0c4)
-
 /* Macros for accessing command fields smaller than 1byte */
 #define DPMAC_MASK(field)        \
        GENMASK(DPMAC_##field##_SHIFT + DPMAC_##field##_SIZE - 1, \
@@ -70,4 +78,54 @@ struct dpmac_rsp_get_counter {
        __le64 counter;
 };
 
+struct dpmac_cmd_set_irq_enable {
+       u8 enable;
+       u8 pad[3];
+       u8 irq_index;
+};
+
+struct dpmac_cmd_get_irq_enable {
+       u32 pad;
+       u8 irq_index;
+};
+
+struct dpmac_rsp_get_irq_enable {
+       u8 enabled;
+};
+
+struct dpmac_cmd_set_irq_mask {
+       u32 mask;
+       u8 irq_index;
+};
+
+struct dpmac_cmd_get_irq_mask {
+       u32 pad;
+       u8 irq_index;
+};
+
+struct dpmac_rsp_get_irq_mask {
+       u32 mask;
+};
+
+struct dpmac_cmd_get_irq_status {
+       u32 status;
+       u8 irq_index;
+};
+
+struct dpmac_rsp_get_irq_status {
+       u32 status;
+};
+
+struct dpmac_cmd_clear_irq_status {
+       u32 status;
+       u8 irq_index;
+};
+
+struct dpmac_rsp_get_link_cfg {
+       u64 options;
+       u32 rate;
+       u32 pad;
+       u64 advertising;
+};
+
 #endif /* _FSL_DPMAC_CMD_H */
index d5997b6..7d6649a 100644 (file)
@@ -181,3 +181,273 @@ int dpmac_get_counter(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
 
        return 0;
 }
+
+/**
+ * dpmac_set_irq_enable() - Set overall interrupt state.
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPMAC object
+ * @irq_index: The interrupt index to configure
+ * @en:                Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes.  The enable/disable control's the
+ * overall interrupt state. if the interrupt is disabled no causes will cause
+ * an interrupt.
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io,
+                        u32 cmd_flags,
+                        u16 token,
+                        u8 irq_index,
+                        u8 en)
+{
+       struct dpmac_cmd_set_irq_enable *cmd_params;
+       struct fsl_mc_command cmd = { 0 };
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_ENABLE,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpmac_cmd_set_irq_enable *)cmd.params;
+       cmd_params->irq_index = irq_index;
+       cmd_params->enable = en;
+
+       /* send command to mc*/
+       return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpmac_get_irq_enable() - Get overall interrupt state
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPMAC object
+ * @irq_index: The interrupt index to configure
+ * @en:                Returned interrupt state - enable = 1, disable = 0
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io,
+                        u32 cmd_flags,
+                        u16 token,
+                        u8 irq_index,
+                        u8 *en)
+{
+       struct dpmac_cmd_get_irq_enable *cmd_params;
+       struct dpmac_rsp_get_irq_enable *rsp_params;
+       struct fsl_mc_command cmd = { 0 };
+       int err;
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_ENABLE,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpmac_cmd_get_irq_enable *)cmd.params;
+       cmd_params->irq_index = irq_index;
+
+       /* send command to mc*/
+       err = mc_send_command(mc_io, &cmd);
+       if (err)
+               return err;
+
+       /* retrieve response parameters */
+       rsp_params = (struct dpmac_rsp_get_irq_enable *)cmd.params;
+       *en = rsp_params->enabled;
+
+       return 0;
+}
+
+/**
+ * dpmac_set_irq_mask() - Set interrupt mask.
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPMAC object
+ * @irq_index: The interrupt index to configure
+ * @mask:      Event mask to trigger interrupt;
+ *             each bit:
+ *                     0 = ignore event
+ *                     1 = consider event for asserting IRQ
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io,
+                      u32 cmd_flags,
+                      u16 token,
+                      u8 irq_index,
+                      u32 mask)
+{
+       struct dpmac_cmd_set_irq_mask *cmd_params;
+       struct fsl_mc_command cmd = { 0 };
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_MASK,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpmac_cmd_set_irq_mask *)cmd.params;
+       cmd_params->mask = cpu_to_le32(mask);
+       cmd_params->irq_index = irq_index;
+
+       /* send command to mc*/
+       return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpmac_get_irq_mask() - Get interrupt mask.
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPMAC object
+ * @irq_index: The interrupt index to configure
+ * @mask:      Returned event mask to trigger interrupt
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io,
+                      u32 cmd_flags,
+                      u16 token,
+                      u8 irq_index,
+                      u32 *mask)
+{
+       struct dpmac_cmd_get_irq_mask *cmd_params;
+       struct dpmac_rsp_get_irq_mask *rsp_params;
+       struct fsl_mc_command cmd = { 0 };
+       int err;
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_MASK,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpmac_cmd_get_irq_mask *)cmd.params;
+       cmd_params->irq_index = irq_index;
+
+       /* send command to mc*/
+       err = mc_send_command(mc_io, &cmd);
+       if (err)
+               return err;
+
+       /* retrieve response parameters */
+       rsp_params = (struct dpmac_rsp_get_irq_mask *)cmd.params;
+       *mask = le32_to_cpu(rsp_params->mask);
+
+       return 0;
+}
+
+/**
+ * dpmac_get_irq_status() - Get the current status of any pending interrupts.
+ *
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPMAC object
+ * @irq_index: The interrupt index to configure
+ * @status:    Returned interrupts status - one bit per cause:
+ *                     0 = no interrupt pending
+ *                     1 = interrupt pending
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpmac_get_irq_status(struct fsl_mc_io *mc_io,
+                        u32 cmd_flags,
+                        u16 token,
+                        u8 irq_index,
+                        u32 *status)
+{
+       struct dpmac_cmd_get_irq_status *cmd_params;
+       struct dpmac_rsp_get_irq_status *rsp_params;
+       struct fsl_mc_command cmd = { 0 };
+       int err;
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_STATUS,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpmac_cmd_get_irq_status *)cmd.params;
+       cmd_params->status = cpu_to_le32(*status);
+       cmd_params->irq_index = irq_index;
+
+       /* send command to mc*/
+       err = mc_send_command(mc_io, &cmd);
+       if (err)
+               return err;
+
+       /* retrieve response parameters */
+       rsp_params = (struct dpmac_rsp_get_irq_status *)cmd.params;
+       *status = le32_to_cpu(rsp_params->status);
+
+       return 0;
+}
+
+/**
+ * dpmac_clear_irq_status() - Clear a pending interrupt's status
+ *
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPMAC object
+ * @irq_index: The interrupt index to configure
+ * @status:    Bits to clear (W1C) - one bit per cause:
+ *                     0 = don't change
+ *                     1 = clear status bit
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io,
+                          u32 cmd_flags,
+                          u16 token,
+                          u8 irq_index,
+                          u32 status)
+{
+       struct dpmac_cmd_clear_irq_status *cmd_params;
+       struct fsl_mc_command cmd = { 0 };
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLEAR_IRQ_STATUS,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpmac_cmd_clear_irq_status *)cmd.params;
+       cmd_params->status = cpu_to_le32(status);
+       cmd_params->irq_index = irq_index;
+
+       /* send command to mc*/
+       return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpmac_get_link_cfg() - Get Ethernet link configuration
+ * @mc_io:      Pointer to opaque I/O object
+ * @cmd_flags:  Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:      Token of DPMAC object
+ * @cfg:        Returned structure with the link configuration
+ *
+ * Return:      '0' on Success; Error code otherwise.
+ */
+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io,
+                     u32 cmd_flags,
+                     u16 token,
+                     struct dpmac_link_cfg *cfg)
+{
+       struct dpmac_rsp_get_link_cfg *rsp_params;
+       struct fsl_mc_command cmd = { 0 };
+       int err = 0;
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_LINK_CFG,
+                                         cmd_flags,
+                                         token);
+
+       /* send command to mc*/
+       err = mc_send_command(mc_io, &cmd);
+       if (err)
+               return err;
+
+       rsp_params = (struct dpmac_rsp_get_link_cfg *)cmd.params;
+       cfg->options = le64_to_cpu(rsp_params->options);
+       cfg->rate = le32_to_cpu(rsp_params->rate);
+       cfg->advertising = le64_to_cpu(rsp_params->advertising);
+
+       return 0;
+}
index 135f143..6022d91 100644 (file)
@@ -118,6 +118,23 @@ int dpmac_get_attributes(struct fsl_mc_io *mc_io,
  */
 #define DPMAC_ADVERTISED_AUTONEG               BIT_ULL(3)
 
+/**
+ * struct dpmac_link_cfg - Structure representing DPMAC link configuration
+ * @rate: Link's rate - in Mbps
+ * @options: Enable/Disable DPMAC link cfg features (bitmap)
+ * @advertising: Speeds that are advertised for autoneg (bitmap)
+ */
+struct dpmac_link_cfg {
+       u32 rate;
+       u64 options;
+       u64 advertising;
+};
+
+int dpmac_get_link_cfg(struct fsl_mc_io *mc_io,
+                      u32 cmd_flags,
+                      u16 token,
+                      struct dpmac_link_cfg *cfg);
+
 /**
  * struct dpmac_link_state - DPMAC link configuration request
  * @rate: Rate in Mbps
@@ -223,4 +240,62 @@ enum dpmac_counter_id {
 int dpmac_get_counter(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
                      enum dpmac_counter_id id, u64 *value);
 
+/**
+ * DPMAC IRQ Index and Events
+ */
+
+/**
+ * IRQ index
+ */
+#define DPMAC_IRQ_INDEX                                0
+/**
+ * IRQ event - indicates a change in link state
+ */
+#define DPMAC_IRQ_EVENT_LINK_CFG_REQ           0x00000001
+/**
+ * IRQ event - Indicates that the link state changed
+ */
+#define DPMAC_IRQ_EVENT_LINK_CHANGED           0x00000002
+/**
+ * IRQ event - Indicate if the phy needs to suspend or resume
+ */
+#define DPMAC_IRQ_EVENT_LINK_UP_REQ            0x00000004
+#define DPMAC_IRQ_EVENT_LINK_DOWN_REQ          0x00000008
+
+int dpmac_set_irq_enable(struct fsl_mc_io *mc_io,
+                        u32 cmd_flags,
+                        u16 token,
+                        u8 irq_index,
+                        u8 en);
+
+int dpmac_get_irq_enable(struct fsl_mc_io *mc_io,
+                        u32 cmd_flags,
+                        u16 token,
+                        u8 irq_index,
+                        u8 *en);
+
+int dpmac_set_irq_mask(struct fsl_mc_io *mc_io,
+                      u32 cmd_flags,
+                      u16 token,
+                      u8 irq_index,
+                      u32 mask);
+
+int dpmac_get_irq_mask(struct fsl_mc_io *mc_io,
+                      u32 cmd_flags,
+                      u16 token,
+                      u8 irq_index,
+                      u32 *mask);
+
+int dpmac_get_irq_status(struct fsl_mc_io *mc_io,
+                        u32 cmd_flags,
+                        u16 token,
+                        u8 irq_index,
+                        u32 *status);
+
+int dpmac_clear_irq_status(struct fsl_mc_io *mc_io,
+                          u32 cmd_flags,
+                          u16 token,
+                          u8 irq_index,
+                          u32 status);
+
 #endif /* __FSL_DPMAC_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/old-mac b/drivers/net/ethernet/freescale/dpaa2/old-mac
new file mode 120000 (symlink)
index 0000000..7d7a17c
--- /dev/null
@@ -0,0 +1 @@
+/home/ioana/work/nxp/git/dash-lts/drivers/staging/fsl-dpaa2/mac/
\ No newline at end of file