Like the PPC platforms, the NXP LS1021A Ethernet controller is managed
by the gianfar driver. Since the LS1021A configuration uses a device tree,
add probing through the device tree to the gianfar driver while preserving
the tree-less PPC support.

Signed-off-by: Renaud Barbier <[email protected]>
---
 arch/powerpc/include/asm/dma.h |  35 ++++-
 drivers/net/Kconfig            |   2 +-
 drivers/net/gianfar.c          | 261 +++++++++++++++++++++++++++++----
 drivers/net/gianfar.h          |  36 +++++
 4 files changed, 300 insertions(+), 34 deletions(-)

diff --git a/arch/powerpc/include/asm/dma.h b/arch/powerpc/include/asm/dma.h
index 27d269f491..dcd9e3d748 100644
--- a/arch/powerpc/include/asm/dma.h
+++ b/arch/powerpc/include/asm/dma.h
@@ -8,6 +8,39 @@
 #ifndef __ASM_DMA_H
 #define __ASM_DMA_H
 
-/* empty */
+#include <io.h>
+
+#define arch_sync_dma_for_cpu arch_sync_dma_for_cpu
+static inline void arch_sync_dma_for_cpu(void *vaddr, size_t size,
+                                        enum dma_data_direction dir)
+{
+}
+
+#define arch_sync_dma_for_device arch_sync_dma_for_device
+static inline void arch_sync_dma_for_device(void *vaddr, size_t size,
+                                           enum dma_data_direction dir)
+{
+}
+
+static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t 
address,
+                                          size_t size, enum dma_data_direction 
dir)
+{
+}
+
+static inline void dma_sync_single_for_device(struct device *dev, dma_addr_t 
address,
+                                             size_t size, enum 
dma_data_direction dir)
+{
+}
+
+static inline dma_addr_t dma_map_single(struct device *dev, void *ptr,
+                                       size_t size, enum dma_data_direction 
dir)
+{
+       return virt_to_phys(ptr);
+}
+
+static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+                                   size_t size, enum dma_data_direction dir)
+{
+}
 
 #endif /* __ASM_DMA_H */
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 068906078c..bd18416d3d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -237,7 +237,7 @@ config DRIVER_NET_FSL_FMAN
 
 config DRIVER_NET_GIANFAR
        bool "Gianfar Ethernet"
-       depends on ARCH_MPC85XX
+       depends on ARCH_MPC85XX || ARCH_LS1021
        select PHYLIB
 
 config DRIVER_NET_KS8851_MLL
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 18b3c8849c..7a947a21da 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -8,14 +8,18 @@
  * based on work by Andy Fleming
  */
 
+#ifdef CONFIG_PPC
 #include <asm/config.h>
+#endif
 #include <common.h>
 #include <malloc.h>
 #include <net.h>
+#include <dma.h>
 #include <init.h>
 #include <driver.h>
 #include <command.h>
 #include <errno.h>
+#include <of_address.h>
 #include <asm/io.h>
 #include <linux/phy.h>
 #include <linux/err.h>
@@ -77,11 +81,11 @@ static void gfar_adjust_link(struct eth_device *edev)
        u32 ecntrl, maccfg2;
 
        priv->link = edev->phydev->link;
-       priv->duplexity =edev->phydev->duplex;
+       priv->duplexity = edev->phydev->duplex;
 
        if (edev->phydev->speed == SPEED_1000)
                priv->speed = 1000;
-       if (edev->phydev->speed == SPEED_100)
+       else if (edev->phydev->speed == SPEED_100)
                priv->speed = 100;
        else
                priv->speed = 10;
@@ -121,11 +125,11 @@ static void gfar_adjust_link(struct eth_device *edev)
                out_be32(regs + GFAR_ECNTRL_OFFSET, ecntrl);
                out_be32(regs + GFAR_MACCFG2_OFFSET, maccfg2);
 
-               dev_info(&edev->dev, "Speed: %d, %s duplex\n", priv->speed,
+               dev_dbg(&edev->dev, "Speed: %d, %s duplex\n", priv->speed,
                       (priv->duplexity) ? "full" : "half");
 
        } else {
-               dev_info(&edev->dev, "No link.\n");
+               dev_dbg(&edev->dev, "No link.\n");
        }
 }
 
@@ -173,28 +177,45 @@ static int gfar_init(struct eth_device *edev)
        return  0;
 }
 
+static int gfar_receive_packets_setup(struct gfar_private *priv, int ix)
+{
+       dma_addr_t dma;
+
+       dma = dma_map_single(priv->dev, priv->rx_buffer[ix], PKTSIZE,
+                       DMA_FROM_DEVICE);
+       if (dma_mapping_error(priv->dev, dma)) {
+               return -EFAULT;
+       }
+
+       out_be32(&priv->rxbd[ix].bufPtr, dma);
+
+       return 0;
+}
+
 static int gfar_open(struct eth_device *edev)
 {
-       int ix;
        struct gfar_private *priv = edev->priv;
        struct gfar_phy *phy = priv->gfar_mdio;
        void __iomem *regs = priv->regs;
-       int ret;
+       int ix, ret;
 
-       ret = phy_device_connect(edev, &phy->miibus, priv->phyaddr,
-                                gfar_adjust_link, 0, PHY_INTERFACE_MODE_NA);
+       ret = phy_device_connect(edev, (phy?&phy->miibus:NULL), priv->phyaddr,
+                                gfar_adjust_link, 0, priv->interface);
        if (ret)
                return ret;
 
        /* Point to the buffer descriptors */
-       out_be32(regs + GFAR_TBASE0_OFFSET, (unsigned int)priv->txbd);
-       out_be32(regs + GFAR_RBASE0_OFFSET, (unsigned int)priv->rxbd);
+       out_be32(regs + GFAR_TBASE0_OFFSET, virt_to_phys(priv->txbd));
+       out_be32(regs + GFAR_RBASE0_OFFSET, virt_to_phys(priv->rxbd));
 
        /* Initialize the Rx Buffer descriptors */
        for (ix = 0; ix < RX_BUF_CNT; ix++) {
                out_be16(&priv->rxbd[ix].status, RXBD_EMPTY);
                out_be16(&priv->rxbd[ix].length, 0);
-               out_be32(&priv->rxbd[ix].bufPtr, (uint) priv->rx_buffer[ix]);
+               if (gfar_receive_packets_setup(priv, ix)) {
+                       dev_err(&edev->dev, "RX packet dma mapping failed");
+                       return -EIO;
+               }
        }
        out_be16(&priv->rxbd[RX_BUF_CNT - 1].status, RXBD_EMPTY | RXBD_WRAP);
 
@@ -324,26 +345,19 @@ static int gfar_bus_reset(struct mii_bus *bus)
        return 0;
 }
 
-/* Reset the external PHYs. */
 static void gfar_init_phy(struct eth_device *dev)
 {
        struct gfar_private *priv = dev->priv;
-       struct gfar_phy *phy = priv->gfar_mdio;
        void __iomem *regs = priv->regs;
-       uint64_t start;
-
-       gfar_local_mdio_write(phy->regs, priv->phyaddr, GFAR_MIIM_CR,
-                       GFAR_MIIM_CR_RST);
-
-       start = get_time_ns();
-       while (!is_timeout(start, 10 * MSECOND)) {
-               if (!(gfar_local_mdio_read(phy->regs, priv->phyaddr,
-                                       GFAR_MIIM_CR) & GFAR_MIIM_CR_RST))
-                       break;
-       }
+       uint32_t ecntrl;
 
-       if (in_be32(regs + GFAR_ECNTRL_OFFSET) & GFAR_ECNTRL_SGMII_MODE)
+       ecntrl = in_be32(regs + GFAR_ECNTRL_OFFSET);
+       if (ecntrl & GFAR_ECNTRL_SGMII_MODE) {
+               priv->interface = PHY_INTERFACE_MODE_SGMII;
                gfar_configure_serdes(priv);
+       } else if (ecntrl & ECNTRL_REDUCED_MODE) {
+               priv->interface = PHY_INTERFACE_MODE_RGMII;
+       }
 }
 
 static int gfar_send(struct eth_device *edev, void *packet, int length)
@@ -351,12 +365,19 @@ static int gfar_send(struct eth_device *edev, void 
*packet, int length)
        struct gfar_private *priv = edev->priv;
        void __iomem *regs = priv->regs;
        struct device *dev = edev->parent;
+       dma_addr_t dma;
        uint64_t start;
        uint tidx;
        uint16_t status;
 
        tidx = priv->txidx;
-       out_be32(&priv->txbd[tidx].bufPtr, (u32) packet);
+
+       dma = dma_map_single(dev, packet, length, DMA_TO_DEVICE);
+       if (dma_mapping_error(priv->dev, dma)) {
+               dev_err(dev, "TX mapping packet failed");
+               return -EFAULT;
+       }
+       out_be32(&priv->txbd[tidx].bufPtr, (u32) dma);
        out_be16(&priv->txbd[tidx].length, length);
        out_be16(&priv->txbd[tidx].status,
                in_be16(&priv->txbd[tidx].status) |
@@ -368,10 +389,11 @@ static int gfar_send(struct eth_device *edev, void 
*packet, int length)
        /* Wait for buffer to be transmitted */
        start = get_time_ns();
        while (in_be16(&priv->txbd[tidx].status) & TXBD_READY) {
-               if (is_timeout(start, 5 * MSECOND)) {
+               if (is_timeout(start, 10 * MSECOND)) {
                        break;
                }
        }
+       dma_unmap_single(dev, dma, length, DMA_TO_DEVICE);
 
        status = in_be16(&priv->txbd[tidx].status);
        if (status & TXBD_READY) {
@@ -401,9 +423,16 @@ static void gfar_recv(struct eth_device *edev)
 
        /* Send the packet up if there were no errors */
        status = in_be16(&priv->rxbd[priv->rxidx].status);
-       if (!(status & RXBD_STATS))
-               net_receive(edev, priv->rx_buffer[priv->rxidx], length - 4);
-       else
+       if (!(status & RXBD_STATS)) {
+               void *frame;
+
+               frame = phys_to_virt(in_be32(&priv->rxbd[priv->rxidx].bufPtr));
+               dma_sync_single_for_cpu(dev, (unsigned long)frame, length,
+                               DMA_FROM_DEVICE);
+               net_receive(edev, frame, length - 4);
+               dma_sync_single_for_device(dev, (unsigned long)frame, length,
+                               DMA_FROM_DEVICE);
+       } else
                dev_err(dev, "Got error %x\n", status & RXBD_STATS);
 
        out_be16(&priv->rxbd[priv->rxidx].length, 0);
@@ -451,7 +480,140 @@ static int gfar_miiphy_write(struct mii_bus *bus, int 
addr, int reg,
        return 0;
 }
 
+#ifdef CONFIG_OFDEVICE
+static int gfar_probe(struct device_d *dev)
+{
+       struct eth_device *edev;
+       struct gfar_private *priv;
+       struct device_node *np;
+       uint32_t tbiaddr = 0x1f;
+       size_t size;
+       void *base;
+       int ret;
+
+       priv = xzalloc(sizeof(struct gfar_private));
+
+       ret = net_alloc_packets(priv->rx_buffer, RX_BUF_CNT);
+       if (ret) {
+               free(priv);
+               return ret;
+       }
+
+       priv->dev = dev;
+       edev = &priv->edev;
+
+       priv->regs = dev_get_mem_region(dev, 0);
+       if (IS_ERR(priv->regs)) {
+               struct device_node *child;
+
+               child = of_get_next_child(dev->device_node, NULL);
+               for_each_child_of_node(dev->device_node, child) {
+                       if (child->name && !strncmp(child->name, "queue-group",
+                                       strlen("queue-group"))) {
+                               priv->regs = of_iomap(child, 0);
+                               if (IS_ERR(priv->regs)) {
+                                       dev_err(dev, "Failed to acquire first 
group address\n");
+                                       return -ENOENT;
+                               } else {
+                                       break;
+                               }
+                       }
+               }
+       }
+       priv->phyaddr = -1;
+
+       np = of_parse_phandle_from(dev->device_node, NULL, "phy-handle", 0);
+       if (np) {
+               uint32_t reg = 0;
+
+               if (!of_property_read_u32(np, "reg", &reg))
+                       priv->phyaddr = reg;
+       } else {
+               dev_err(dev, "Could not get phy-handle address\n");
+               return -ENOENT;
+       }
+
+       priv->tbicr = TSEC_TBICR_SETTINGS;
+       priv->tbiana = TBIANA_SETTINGS;
+
+       /* Handle to tbi node */
+       np = of_parse_phandle_from(dev->device_node, NULL, "tbi-handle", 0);
+       if (np) {
+               struct device_node *parent;
+               struct gfar_phy *tbiphy;
+               struct device *bus_dev;
+               struct mii_bus *bus;
+
+               /* Get tbi address to be programmed in device */
+               if (of_property_read_u32(np, "reg", &tbiaddr)) {
+                       dev_err(dev, "Failed to get tbi reg property\n");
+                       return -ENOENT;
+               }
+               /* MDIO is the parent  */
+               parent = of_get_parent(np);
+               if (parent == NULL)  {
+                       dev_err(dev, "No parent node for TBI PHY?\n");
+                       return -ENOENT;
+               }
+               tbiphy = xzalloc(sizeof(*tbiphy));
+               tbiphy->dev = parent->dev;
+               tbiphy->regs = NULL;
+
+               for_each_mii_bus(bus) {
+                       if (bus->dev.of_node == parent) {
+                               struct gfar_phy *phy;
+
+                               bus_dev = bus->parent;
+                               phy = bus_dev->priv;
+                               tbiphy->regs = phy->regs;
+                               break;
+                       }
+               }
+               if (tbiphy->regs == NULL) {
+                       dev_err(dev, "Could not get TBI address\n");
+                       free(tbiphy);
+                       return PTR_ERR(tbiphy->regs);
+               }
+
+               tbiphy->miibus.read = gfar_miiphy_read;
+               tbiphy->miibus.write = gfar_miiphy_write;
+               tbiphy->miibus.priv = tbiphy;
+               tbiphy->miibus.parent = dev;
+               tbiphy->dev->priv = tbiphy;
+               priv->gfar_tbi = tbiphy;
+       }
+
+       priv->tbiaddr = tbiaddr;
+       out_be32(priv->regs + GFAR_TBIPA_OFFSET, priv->tbiaddr);
+
+       size = ((TX_BUF_CNT * sizeof(struct txbd8)) +
+               (RX_BUF_CNT * sizeof(struct rxbd8)));
+       base = dma_alloc_coherent(DMA_DEVICE_BROKEN, size, DMA_ADDRESS_BROKEN);
+       priv->txbd = (struct txbd8 __iomem *)base;
+       priv->rxbd = (struct rxbd8 __iomem *)(base + (TX_BUF_CNT * 
sizeof(struct txbd8)));
+       dma_set_mask(dev, DMA_BIT_MASK(32));
+
+       edev->priv = priv;
+       edev->init = gfar_init;
+       edev->open = gfar_open;
+       edev->halt = gfar_halt;
+       edev->send = gfar_send;
+       edev->recv = gfar_recv;
+       edev->get_ethaddr = gfar_get_ethaddr;
+       edev->set_ethaddr = gfar_set_ethaddr;
+       edev->parent = dev;
+
+       setbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET);
+       udelay(2);
+       clrbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET);
+
+       gfar_init_phy(edev);
+
+       return eth_register(edev);
+}
+#else
 /*
+ * PPC only as there is no device tree support.
  * Initialize device structure. Returns success if
  * initialization succeeded.
  */
@@ -469,8 +631,10 @@ static int gfar_probe(struct device *dev)
        priv = xzalloc(sizeof(struct gfar_private));
 
        ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer));
-       if (ret)
+       if (ret) {
+               free(priv);
                return ret;
+       }
 
        edev = &priv->edev;
 
@@ -527,21 +691,49 @@ static int gfar_probe(struct device *dev)
 
        return eth_register(edev);
 }
+#endif
+
+static struct tsec_data etsec2_data = {
+       .mdio_regs_off = 0x520,
+};
+
+static struct tsec_data gianfar_data = {
+       .mdio_regs_off = 0x0,
+};
+
+static const struct of_device_id gfar_ids[] = {
+       { .compatible = "fsl,etsec2", .data = &etsec2_data },
+       { .compatible = "gianfar", .data = &gianfar_data },
+       { /* sentinel */ }
+};
 
 static struct driver gfar_eth_driver = {
        .name  = "gfar",
+       .of_compatible = DRV_OF_COMPAT(gfar_ids),
        .probe = gfar_probe,
 };
 device_platform_driver(gfar_eth_driver);
 
+/* MII bus */
+static struct fsl_pq_mdio_data mdio_gianfar_data = {
+       .mdio_regs_off = 0x0,
+};
+
+static __maybe_unused const struct of_device_id gfar_mdio_ids[] = {
+       { .compatible = "fsl,etsec2-mdio", .data = &mdio_gianfar_data },
+       { /* sentinel */ }
+};
+
 static int gfar_phy_probe(struct device *dev)
 {
        struct gfar_phy *phy;
+       struct fsl_pq_mdio_data *data;
        int ret;
 
+       data = (struct fsl_pq_mdio_data *)device_get_match_data(dev);
        phy = xzalloc(sizeof(*phy));
        phy->dev = dev;
-       phy->regs = dev_get_mem_region(dev, 0);
+       phy->regs = dev_get_mem_region(dev, 0) + data->mdio_regs_off;
        if (IS_ERR(phy->regs))
                return PTR_ERR(phy->regs);
 
@@ -561,10 +753,14 @@ static int gfar_phy_probe(struct device *dev)
 
 static struct driver gfar_phy_driver = {
        .name  = "gfar-mdio",
+#ifdef CONFIG_OFDEVICE
+       .of_compatible = DRV_OF_COMPAT(gfar_mdio_ids),
+#endif
        .probe = gfar_phy_probe,
 };
 register_driver_macro(coredevice, platform, gfar_phy_driver);
 
+#ifndef CONFIG_OFDEVICE
 static int gfar_tbiphy_probe(struct device *dev)
 {
        struct gfar_phy *phy;
@@ -594,3 +790,4 @@ static struct driver gfar_tbiphy_driver = {
        .probe = gfar_tbiphy_probe,
 };
 register_driver_macro(coredevice, platform, gfar_tbiphy_driver);
+#endif
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 8ccbbe1473..42add4df87 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -14,8 +14,14 @@
 #define __GIANFAR_H
 
 #include <net.h>
+
+#ifdef CONFIG_PPC
 #include <asm/config.h>
+#endif
+
+#ifdef CONFIG_ARCH_MPC85XX
 #include <mach/gianfar.h>
+#endif
 
 #define MAC_ADDR_LEN 6
 
@@ -27,6 +33,10 @@
 #define GFAR_TBI_ANEX          0x06
 #define GFAR_TBI_TBICON                0x11
 
+#ifndef GFAR_TBIPA_OFFSET
+#define GFAR_TBIPA_OFFSET       0x030
+#endif
+
 /* TBI MDIO register bit fields*/
 #define GFAR_TBICON_CLK_SELECT         0x0020
 #define GFAR_TBIANA_ASYMMETRIC_PAUSE   0x0100
@@ -64,7 +74,9 @@
 
 #define ECNTRL_INIT_SETTINGS           0x00001000
 #define GFAR_ECNTRL_TBI_MODE           0x00000020
+#define ECNTRL_REDUCED_MODE            0x00000010
 #define GFAR_ECNTRL_R100               0x00000008
+#define GFAR_ECNTRL_RMM                        0x00000004
 #define GFAR_ECNTRL_SGMII_MODE         0x00000002
 
 #ifndef GFAR_TBIPA_VALUE
@@ -271,9 +283,11 @@ struct gfar_phy {
 #define BUF_ALIGN      8
 
 struct gfar_private {
+       struct device_d *dev;
        struct eth_device edev;
        void __iomem *regs;
        int mdiobus_tbi;
+       phy_interface_t interface;
        struct gfar_phy *gfar_mdio;
        struct gfar_phy *gfar_tbi;
        struct phy_info *phyinfo;
@@ -284,9 +298,31 @@ struct gfar_private {
        uint phyaddr;
        uint tbicr;
        uint tbiana;
+       uint tbiaddr;
        uint link;
        uint duplexity;
        uint speed;
        void *rx_buffer[PKTBUFSRX];
 };
+
+struct tsec_data {
+       uint32_t mdio_regs_off;
+};
+
+struct fsl_pq_mdio_data {
+       uint32_t mdio_regs_off;
+};
+
+#define TBIANA_SETTINGS ( \
+       GFAR_TBIANA_ASYMMETRIC_PAUSE \
+       | GFAR_TBIANA_SYMMETRIC_PAUSE \
+       | GFAR_TBIANA_FULL_DUPLEX \
+       )
+
+#define TSEC_TBICR_SETTINGS ( \
+       GFAR_TBICR_PHY_RESET \
+       | GFAR_TBICR_ANEG_ENABLE \
+       | GFAR_TBICR_FULL_DUPLEX \
+       | GFAR_TBICR_SPEED1_SET \
+       )
 #endif /* __GIANFAR_H */
-- 
2.43.0


Reply via email to