linux/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
<<
>>
Prefs
   1/*******************************************************************************
   2  This contains the functions to handle the normal descriptors.
   3
   4  Copyright (C) 2007-2009  STMicroelectronics Ltd
   5
   6  This program is free software; you can redistribute it and/or modify it
   7  under the terms and conditions of the GNU General Public License,
   8  version 2, as published by the Free Software Foundation.
   9
  10  This program is distributed in the hope it will be useful, but WITHOUT
  11  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13  more details.
  14
  15  You should have received a copy of the GNU General Public License along with
  16  this program; if not, write to the Free Software Foundation, Inc.,
  17  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  18
  19  The full GNU General Public License is included in this distribution in
  20  the file called "COPYING".
  21
  22  Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
  23*******************************************************************************/
  24
  25#include <linux/stmmac.h>
  26#include "common.h"
  27#include "descs_com.h"
  28
  29static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
  30                               struct dma_desc *p, void __iomem *ioaddr)
  31{
  32        struct net_device_stats *stats = (struct net_device_stats *)data;
  33        unsigned int tdes0 = p->des0;
  34        unsigned int tdes1 = p->des1;
  35        int ret = tx_done;
  36
  37        /* Get tx owner first */
  38        if (unlikely(tdes0 & TDES0_OWN))
  39                return tx_dma_own;
  40
  41        /* Verify tx error by looking at the last segment. */
  42        if (likely(!(tdes1 & TDES1_LAST_SEGMENT)))
  43                return tx_not_ls;
  44
  45        if (unlikely(tdes0 & TDES0_ERROR_SUMMARY)) {
  46                if (unlikely(tdes0 & TDES0_UNDERFLOW_ERROR)) {
  47                        x->tx_underflow++;
  48                        stats->tx_fifo_errors++;
  49                }
  50                if (unlikely(tdes0 & TDES0_NO_CARRIER)) {
  51                        x->tx_carrier++;
  52                        stats->tx_carrier_errors++;
  53                }
  54                if (unlikely(tdes0 & TDES0_LOSS_CARRIER)) {
  55                        x->tx_losscarrier++;
  56                        stats->tx_carrier_errors++;
  57                }
  58                if (unlikely((tdes0 & TDES0_EXCESSIVE_DEFERRAL) ||
  59                             (tdes0 & TDES0_EXCESSIVE_COLLISIONS) ||
  60                             (tdes0 & TDES0_LATE_COLLISION))) {
  61                        unsigned int collisions;
  62
  63                        collisions = (tdes0 & TDES0_COLLISION_COUNT_MASK) >> 3;
  64                        stats->collisions += collisions;
  65                }
  66                ret = tx_err;
  67        }
  68
  69        if (tdes0 & TDES0_VLAN_FRAME)
  70                x->tx_vlan++;
  71
  72        if (unlikely(tdes0 & TDES0_DEFERRED))
  73                x->tx_deferred++;
  74
  75        return ret;
  76}
  77
  78static int ndesc_get_tx_len(struct dma_desc *p)
  79{
  80        return (p->des1 & RDES1_BUFFER1_SIZE_MASK);
  81}
  82
  83/* This function verifies if each incoming frame has some errors
  84 * and, if required, updates the multicast statistics.
  85 * In case of success, it returns good_frame because the GMAC device
  86 * is supposed to be able to compute the csum in HW. */
  87static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
  88                               struct dma_desc *p)
  89{
  90        int ret = good_frame;
  91        unsigned int rdes0 = p->des0;
  92        struct net_device_stats *stats = (struct net_device_stats *)data;
  93
  94        if (unlikely(rdes0 & RDES0_OWN))
  95                return dma_own;
  96
  97        if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
  98                pr_warn("%s: Oversized frame spanned multiple buffers\n",
  99                        __func__);
 100                stats->rx_length_errors++;
 101                return discard_frame;
 102        }
 103
 104        if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
 105                if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR))
 106                        x->rx_desc++;
 107                if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL))
 108                        x->sa_filter_fail++;
 109                if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR))
 110                        x->overflow_error++;
 111                if (unlikely(rdes0 & RDES0_IPC_CSUM_ERROR))
 112                        x->ipc_csum_error++;
 113                if (unlikely(rdes0 & RDES0_COLLISION)) {
 114                        x->rx_collision++;
 115                        stats->collisions++;
 116                }
 117                if (unlikely(rdes0 & RDES0_CRC_ERROR)) {
 118                        x->rx_crc++;
 119                        stats->rx_crc_errors++;
 120                }
 121                ret = discard_frame;
 122        }
 123        if (unlikely(rdes0 & RDES0_DRIBBLING))
 124                x->dribbling_bit++;
 125
 126        if (unlikely(rdes0 & RDES0_LENGTH_ERROR)) {
 127                x->rx_length++;
 128                ret = discard_frame;
 129        }
 130        if (unlikely(rdes0 & RDES0_MII_ERROR)) {
 131                x->rx_mii++;
 132                ret = discard_frame;
 133        }
 134#ifdef STMMAC_VLAN_TAG_USED
 135        if (rdes0 & RDES0_VLAN_TAG)
 136                x->vlan_tag++;
 137#endif
 138        return ret;
 139}
 140
 141static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
 142                               int end)
 143{
 144        p->des0 |= RDES0_OWN;
 145        p->des1 |= (BUF_SIZE_2KiB - 1) & RDES1_BUFFER1_SIZE_MASK;
 146
 147        if (mode == STMMAC_CHAIN_MODE)
 148                ndesc_rx_set_on_chain(p, end);
 149        else
 150                ndesc_rx_set_on_ring(p, end);
 151
 152        if (disable_rx_ic)
 153                p->des1 |= RDES1_DISABLE_IC;
 154}
 155
 156static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)
 157{
 158        p->des0 &= ~TDES0_OWN;
 159        if (mode == STMMAC_CHAIN_MODE)
 160                ndesc_tx_set_on_chain(p);
 161        else
 162                ndesc_end_tx_desc_on_ring(p, end);
 163}
 164
 165static int ndesc_get_tx_owner(struct dma_desc *p)
 166{
 167        return (p->des0 & TDES0_OWN) >> 31;
 168}
 169
 170static void ndesc_set_tx_owner(struct dma_desc *p)
 171{
 172        p->des0 |= TDES0_OWN;
 173}
 174
 175static void ndesc_set_rx_owner(struct dma_desc *p)
 176{
 177        p->des0 |= RDES0_OWN;
 178}
 179
 180static int ndesc_get_tx_ls(struct dma_desc *p)
 181{
 182        return (p->des1 & TDES1_LAST_SEGMENT) >> 30;
 183}
 184
 185static void ndesc_release_tx_desc(struct dma_desc *p, int mode)
 186{
 187        int ter = (p->des1 & TDES1_END_RING) >> 25;
 188
 189        memset(p, 0, offsetof(struct dma_desc, des2));
 190        if (mode == STMMAC_CHAIN_MODE)
 191                ndesc_tx_set_on_chain(p);
 192        else
 193                ndesc_end_tx_desc_on_ring(p, ter);
 194}
 195
 196static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
 197                                  bool csum_flag, int mode, bool tx_own,
 198                                  bool ls)
 199{
 200        unsigned int tdes1 = p->des1;
 201
 202        if (is_fs)
 203                tdes1 |= TDES1_FIRST_SEGMENT;
 204        else
 205                tdes1 &= ~TDES1_FIRST_SEGMENT;
 206
 207        if (likely(csum_flag))
 208                tdes1 |= (TX_CIC_FULL) << TDES1_CHECKSUM_INSERTION_SHIFT;
 209        else
 210                tdes1 &= ~(TX_CIC_FULL << TDES1_CHECKSUM_INSERTION_SHIFT);
 211
 212        if (ls)
 213                tdes1 |= TDES1_LAST_SEGMENT;
 214
 215        p->des1 = tdes1;
 216
 217        if (mode == STMMAC_CHAIN_MODE)
 218                norm_set_tx_desc_len_on_chain(p, len);
 219        else
 220                norm_set_tx_desc_len_on_ring(p, len);
 221
 222        if (tx_own)
 223                p->des0 |= TDES0_OWN;
 224}
 225
 226static void ndesc_set_tx_ic(struct dma_desc *p)
 227{
 228        p->des1 |= TDES1_INTERRUPT;
 229}
 230
 231static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
 232{
 233        unsigned int csum = 0;
 234
 235        /* The type-1 checksum offload engines append the checksum at
 236         * the end of frame and the two bytes of checksum are added in
 237         * the length.
 238         * Adjust for that in the framelen for type-1 checksum offload
 239         * engines
 240         */
 241        if (rx_coe_type == STMMAC_RX_COE_TYPE1)
 242                csum = 2;
 243
 244        return (((p->des0 & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT) -
 245                csum);
 246
 247}
 248
 249static void ndesc_enable_tx_timestamp(struct dma_desc *p)
 250{
 251        p->des1 |= TDES1_TIME_STAMP_ENABLE;
 252}
 253
 254static int ndesc_get_tx_timestamp_status(struct dma_desc *p)
 255{
 256        return (p->des0 & TDES0_TIME_STAMP_STATUS) >> 17;
 257}
 258
 259static u64 ndesc_get_timestamp(void *desc, u32 ats)
 260{
 261        struct dma_desc *p = (struct dma_desc *)desc;
 262        u64 ns;
 263
 264        ns = p->des2;
 265        /* convert high/sec time stamp value to nanosecond */
 266        ns += p->des3 * 1000000000ULL;
 267
 268        return ns;
 269}
 270
 271static int ndesc_get_rx_timestamp_status(void *desc, u32 ats)
 272{
 273        struct dma_desc *p = (struct dma_desc *)desc;
 274
 275        if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
 276                /* timestamp is corrupted, hence don't store it */
 277                return 0;
 278        else
 279                return 1;
 280}
 281
 282const struct stmmac_desc_ops ndesc_ops = {
 283        .tx_status = ndesc_get_tx_status,
 284        .rx_status = ndesc_get_rx_status,
 285        .get_tx_len = ndesc_get_tx_len,
 286        .init_rx_desc = ndesc_init_rx_desc,
 287        .init_tx_desc = ndesc_init_tx_desc,
 288        .get_tx_owner = ndesc_get_tx_owner,
 289        .release_tx_desc = ndesc_release_tx_desc,
 290        .prepare_tx_desc = ndesc_prepare_tx_desc,
 291        .set_tx_ic = ndesc_set_tx_ic,
 292        .get_tx_ls = ndesc_get_tx_ls,
 293        .set_tx_owner = ndesc_set_tx_owner,
 294        .set_rx_owner = ndesc_set_rx_owner,
 295        .get_rx_frame_len = ndesc_get_rx_frame_len,
 296        .enable_tx_timestamp = ndesc_enable_tx_timestamp,
 297        .get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
 298        .get_timestamp = ndesc_get_timestamp,
 299        .get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
 300};
 301