]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/net/fec_mxc.c
merged denx master branch
[karo-tx-uboot.git] / drivers / net / fec_mxc.c
index 156fa8f916a3666d7e8bad371b2bcdf720081c27..faf065a7f0995725f3ead40133c960f714e77207 100644 (file)
@@ -42,6 +42,14 @@ DECLARE_GLOBAL_DATA_PTR;
 #define CONFIG_FEC_XCV_TYPE MII100
 #endif
 
+#if !defined(CONDIF_SYS_DCACHE_OFF) && !defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
+/* Due to multiple RX and TX buffer descriptors sharing a cache line
+ * the driver can only work with DMA coherent memory.
+ * Since U-Boot does not provide this, cache must be disabled or
+ * write-through.
+ */
+#error This driver cannot be used with Writeback DCACHE
+#endif
 /*
  * The i.MX28 operates with packets in big endian. We need to swap them before
  * sending and after receiving.
@@ -247,26 +255,34 @@ static int miiphy_wait_aneg(struct eth_device *dev)
 }
 #endif
 
-static int fec_rx_task_enable(struct fec_priv *fec)
+static inline void fec_rx_task_enable(struct fec_priv *fec)
 {
        writel(1 << 24, &fec->eth->r_des_active);
-       return 0;
 }
 
-static int fec_rx_task_disable(struct fec_priv *fec)
+static inline void fec_rx_task_disable(struct fec_priv *fec)
 {
-       return 0;
 }
 
-static int fec_tx_task_enable(struct fec_priv *fec)
+static inline void fec_tx_task_enable(struct fec_priv *fec)
 {
        writel(1 << 24, &fec->eth->x_des_active);
-       return 0;
 }
 
-static int fec_tx_task_disable(struct fec_priv *fec)
+static inline void fec_tx_task_disable(struct fec_priv *fec)
 {
-       return 0;
+}
+
+static inline void fec_invalidate_bd(struct fec_bd *bd)
+{
+       invalidate_dcache_range((unsigned long)bd,
+                               (unsigned long)bd + sizeof(*bd));
+}
+
+static inline void fec_flush_bd(struct fec_bd *bd)
+{
+       flush_dcache_range((unsigned long)bd,
+                       (unsigned long)bd + sizeof(*bd));
 }
 
 /**
@@ -339,7 +355,9 @@ static void fec_tbd_init(struct fec_priv *fec)
        unsigned size = roundup(2 * sizeof(struct fec_bd),
                                ARCH_DMA_MINALIGN);
        writew(0x0000, &fec->tbd_base[0].status);
+       fec_flush_bd(&fec->tbd_base[0]);
        writew(FEC_TBD_WRAP, &fec->tbd_base[1].status);
+       fec_flush_bd(&fec->tbd_base[1]);
        fec->tbd_index = 0;
        flush_dcache_range(addr, addr+size);
 }
@@ -442,7 +460,7 @@ static void fec_reg_setup(struct fec_priv *fec)
  */
 static int fec_open(struct eth_device *edev)
 {
-       struct fec_priv *fec = (struct fec_priv *)edev->priv;
+       struct fec_priv *fec = edev->priv;
        int speed;
        uint32_t addr, size;
        int i;
@@ -690,6 +708,7 @@ static void fec_halt(struct eth_device *dev)
 static int fec_send(struct eth_device *dev, volatile void *packet, int length)
 {
        unsigned int status;
+       int timeout = 1000;
        uint32_t size;
        uint32_t addr;
 
@@ -697,7 +716,7 @@ static int fec_send(struct eth_device *dev, volatile void *packet, int length)
         * This routine transmits one frame.  This routine only accepts
         * 6-byte Ethernet addresses.
         */
-       struct fec_priv *fec = (struct fec_priv *)dev->priv;
+       struct fec_priv *fec = dev->priv;
 
        /*
         * Check for valid length of data.
@@ -727,13 +746,14 @@ static int fec_send(struct eth_device *dev, volatile void *packet, int length)
         * update BD's status now
         * This block:
         * - is always the last in a chain (means no chain)
-        * - should transmitt the CRC
+        * - should transmit the CRC
         * - might be the last BD in the list, so the address counter should
         *   wrap (-> keep the WRAP flag)
         */
        status = readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_WRAP;
        status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY;
        writew(status, &fec->tbd_base[fec->tbd_index].status);
+       fec_flush_bd(&fec->tbd_base[fec->tbd_index]);
 
        /*
         * Flush data cache. This code flushes both TX descriptors to RAM.
@@ -756,6 +776,8 @@ static int fec_send(struct eth_device *dev, volatile void *packet, int length)
         */
        invalidate_dcache_range(addr, addr + size);
        while (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY) {
+               if (--timeout < 0)
+                       return -ETIMEDOUT;
                udelay(1);
                invalidate_dcache_range(addr, addr + size);
        }
@@ -845,6 +867,11 @@ static int fec_recv(struct eth_device *dev)
                         */
                        frame = (struct nbuf *)readl(&rbd->data_pointer);
                        frame_length = readw(&rbd->data_length) - 4;
+
+                       invalidate_dcache_range((unsigned long)frame,
+                                               (unsigned long)frame +
+                                               sizeof(*frame));
+
                        /*
                         * Invalidate data cache over the buffer
                         */
@@ -904,23 +931,20 @@ static int fec_probe(bd_t *bd, int dev_id, int phy_id, uint32_t base_addr)
        int ret = 0;
 
        /* create and fill edev struct */
-       edev = (struct eth_device *)malloc(sizeof(struct eth_device));
+       edev = calloc(sizeof(struct eth_device), 1);
        if (!edev) {
                puts("fec_mxc: not enough malloc memory for eth_device\n");
                ret = -ENOMEM;
                goto err1;
        }
 
-       fec = (struct fec_priv *)malloc(sizeof(struct fec_priv));
+       fec = calloc(sizeof(struct fec_priv), 1);
        if (!fec) {
                puts("fec_mxc: not enough malloc memory for fec_priv\n");
                ret = -ENOMEM;
                goto err2;
        }
 
-       memset(edev, 0, sizeof(*edev));
-       memset(fec, 0, sizeof(*fec));
-
        edev->priv = fec;
        edev->init = fec_init;
        edev->send = fec_send;