net: ll_temac: Add support for non-native register endianness
authorEsben Haabendal <esben@geanix.com>
Tue, 30 Apr 2019 07:17:51 +0000 (09:17 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 1 May 2019 18:33:30 +0000 (14:33 -0400)
Replace the powerpc specific MMIO register access functions with the
generic big-endian mmio access functions, and add support for
little-endian access depending on configuration.

Big-endian access is maintained as the default, but little-endian can
be configured in device-tree binding or in platform data.

The temac_ior()/temac_iow() functions are replaced with macro wrappers
to avoid modifying existing code more than necessary.

Signed-off-by: Esben Haabendal <esben@geanix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/xilinx/ll_temac.h
drivers/net/ethernet/xilinx/ll_temac_main.c
include/linux/platform_data/xilinx-ll-temac.h

index e338b4fa3c60c9b4dfc1ee66a324308b9018b932..23d8dd5330f8ce442213aa5b98d4485f5a598081 100644 (file)
@@ -347,8 +347,10 @@ struct temac_local {
 #ifdef CONFIG_PPC_DCR
        dcr_host_t sdma_dcrs;
 #endif
-       u32 (*dma_in)(struct temac_local *, int);
-       void (*dma_out)(struct temac_local *, int, u32);
+       u32 (*temac_ior)(struct temac_local *lp, int offset);
+       void (*temac_iow)(struct temac_local *lp, int offset, u32 value);
+       u32 (*dma_in)(struct temac_local *lp, int reg);
+       void (*dma_out)(struct temac_local *lp, int reg, u32 value);
 
        int tx_irq;
        int rx_irq;
@@ -372,9 +374,11 @@ struct temac_local {
        int rx_bd_ci;
 };
 
+/* Wrappers for temac_ior()/temac_iow() function pointers above */
+#define temac_ior(lp, o) ((lp)->temac_ior(lp, o))
+#define temac_iow(lp, o, v) ((lp)->temac_iow(lp, o, v))
+
 /* xilinx_temac.c */
-u32 temac_ior(struct temac_local *lp, int offset);
-void temac_iow(struct temac_local *lp, int offset, u32 value);
 int temac_indirect_busywait(struct temac_local *lp);
 u32 temac_indirect_in32(struct temac_local *lp, int reg);
 void temac_indirect_out32(struct temac_local *lp, int reg, u32 value);
index bcafb8925f75de53a2669d800d64a666057026e9..58c67138ed2b9e89ae7fe00034da2487181f8df7 100644 (file)
  * Low level register access functions
  */
 
-u32 temac_ior(struct temac_local *lp, int offset)
+u32 _temac_ior_be(struct temac_local *lp, int offset)
 {
-       return in_be32(lp->regs + offset);
+       return ioread32be(lp->regs + offset);
 }
 
-void temac_iow(struct temac_local *lp, int offset, u32 value)
+void _temac_iow_be(struct temac_local *lp, int offset, u32 value)
 {
-       out_be32(lp->regs + offset, value);
+       return iowrite32be(value, lp->regs + offset);
+}
+
+u32 _temac_ior_le(struct temac_local *lp, int offset)
+{
+       return ioread32(lp->regs + offset);
+}
+
+void _temac_iow_le(struct temac_local *lp, int offset, u32 value)
+{
+       return iowrite32(value, lp->regs + offset);
 }
 
 int temac_indirect_busywait(struct temac_local *lp)
@@ -121,23 +131,35 @@ void temac_indirect_out32(struct temac_local *lp, int reg, u32 value)
 }
 
 /**
- * temac_dma_in32 - Memory mapped DMA read, this function expects a
- * register input that is based on DCR word addresses which
- * are then converted to memory mapped byte addresses
+ * temac_dma_in32_* - Memory mapped DMA read, these function expects a
+ * register input that is based on DCR word addresses which are then
+ * converted to memory mapped byte addresses.  To be assigned to
+ * lp->dma_in32.
  */
-static u32 temac_dma_in32(struct temac_local *lp, int reg)
+static u32 temac_dma_in32_be(struct temac_local *lp, int reg)
 {
-       return in_be32(lp->sdma_regs + (reg << 2));
+       return ioread32be(lp->sdma_regs + (reg << 2));
+}
+
+static u32 temac_dma_in32_le(struct temac_local *lp, int reg)
+{
+       return ioread32(lp->sdma_regs + (reg << 2));
 }
 
 /**
- * temac_dma_out32 - Memory mapped DMA read, this function expects a
- * register input that is based on DCR word addresses which
- * are then converted to memory mapped byte addresses
+ * temac_dma_out32_* - Memory mapped DMA read, these function expects
+ * a register input that is based on DCR word addresses which are then
+ * converted to memory mapped byte addresses.  To be assigned to
+ * lp->dma_out32.
  */
-static void temac_dma_out32(struct temac_local *lp, int reg, u32 value)
+static void temac_dma_out32_be(struct temac_local *lp, int reg, u32 value)
+{
+       iowrite32be(value, lp->sdma_regs + (reg << 2));
+}
+
+static void temac_dma_out32_le(struct temac_local *lp, int reg, u32 value)
 {
-       out_be32(lp->sdma_regs + (reg << 2), value);
+       iowrite32(value, lp->sdma_regs + (reg << 2));
 }
 
 /* DMA register access functions can be DCR based or memory mapped.
@@ -1024,6 +1046,7 @@ static int temac_probe(struct platform_device *pdev)
        struct resource *res;
        const void *addr;
        __be32 *p;
+       bool little_endian;
        int rc = 0;
 
        /* Init network device structure */
@@ -1068,6 +1091,24 @@ static int temac_probe(struct platform_device *pdev)
                return PTR_ERR(lp->regs);
        }
 
+       /* Select register access functions with the specified
+        * endianness mode.  Default for OF devices is big-endian.
+        */
+       little_endian = false;
+       if (temac_np) {
+               if (of_get_property(temac_np, "little-endian", NULL))
+                       little_endian = true;
+       } else if (pdata) {
+               little_endian = pdata->reg_little_endian;
+       }
+       if (little_endian) {
+               lp->temac_ior = _temac_ior_le;
+               lp->temac_iow = _temac_iow_le;
+       } else {
+               lp->temac_ior = _temac_ior_be;
+               lp->temac_iow = _temac_iow_be;
+       }
+
        /* Setup checksum offload, but default to off if not specified */
        lp->temac_features = 0;
        if (temac_np) {
@@ -1111,8 +1152,13 @@ static int temac_probe(struct platform_device *pdev)
                                of_node_put(dma_np);
                                return PTR_ERR(lp->sdma_regs);
                        }
-                       lp->dma_in = temac_dma_in32;
-                       lp->dma_out = temac_dma_out32;
+                       if (of_get_property(dma_np, "little-endian", NULL)) {
+                               lp->dma_in = temac_dma_in32_le;
+                               lp->dma_out = temac_dma_out32_le;
+                       } else {
+                               lp->dma_in = temac_dma_in32_be;
+                               lp->dma_out = temac_dma_out32_be;
+                       }
                        dev_dbg(&pdev->dev, "MEM base: %p\n", lp->sdma_regs);
                }
 
@@ -1132,8 +1178,13 @@ static int temac_probe(struct platform_device *pdev)
                                "could not map DMA registers\n");
                        return PTR_ERR(lp->sdma_regs);
                }
-               lp->dma_in = temac_dma_in32;
-               lp->dma_out = temac_dma_out32;
+               if (pdata->dma_little_endian) {
+                       lp->dma_in = temac_dma_in32_le;
+                       lp->dma_out = temac_dma_out32_le;
+               } else {
+                       lp->dma_in = temac_dma_in32_be;
+                       lp->dma_out = temac_dma_out32_be;
+               }
 
                /* Get DMA RX and TX interrupts */
                lp->rx_irq = platform_get_irq(pdev, 0);
index 82e2f80648b00c61c95e542b04ae8be4078d489b..af87927abab325030f80d622c4ee05cf472238ab 100644 (file)
@@ -14,6 +14,8 @@ struct ll_temac_platform_data {
        unsigned long long mdio_bus_id; /* Unique id for MDIO bus */
        int phy_addr;           /* Address of the PHY to connect to */
        phy_interface_t phy_interface; /* PHY interface mode */
+       bool reg_little_endian; /* Little endian TEMAC register access  */
+       bool dma_little_endian; /* Little endian DMA register access  */
 };
 
 #endif /* __LINUX_XILINX_LL_TEMAC_H */