linux/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
<<
>>
Prefs
   1/*******************************************************************************
   2  This is the driver for the GMAC on-chip Ethernet controller for ST SoCs.
   3  DWC Ether MAC 10/100/1000 Universal version 3.41a  has been used for
   4  developing this code.
   5
   6  This only implements the mac core functions for this chip.
   7
   8  Copyright (C) 2007-2009  STMicroelectronics Ltd
   9
  10  This program is free software; you can redistribute it and/or modify it
  11  under the terms and conditions of the GNU General Public License,
  12  version 2, as published by the Free Software Foundation.
  13
  14  This program is distributed in the hope it will be useful, but WITHOUT
  15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  17  more details.
  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/crc32.h>
  26#include <linux/slab.h>
  27#include <linux/ethtool.h>
  28#include <asm/io.h>
  29#include "stmmac_pcs.h"
  30#include "dwmac1000.h"
  31
  32static void dwmac1000_core_init(struct mac_device_info *hw, int mtu)
  33{
  34        void __iomem *ioaddr = hw->pcsr;
  35        u32 value = readl(ioaddr + GMAC_CONTROL);
  36
  37        /* Configure GMAC core */
  38        value |= GMAC_CORE_INIT;
  39
  40        if (mtu > 1500)
  41                value |= GMAC_CONTROL_2K;
  42        if (mtu > 2000)
  43                value |= GMAC_CONTROL_JE;
  44
  45        if (hw->ps) {
  46                value |= GMAC_CONTROL_TE;
  47
  48                value &= ~hw->link.speed_mask;
  49                switch (hw->ps) {
  50                case SPEED_1000:
  51                        value |= hw->link.speed1000;
  52                        break;
  53                case SPEED_100:
  54                        value |= hw->link.speed100;
  55                        break;
  56                case SPEED_10:
  57                        value |= hw->link.speed10;
  58                        break;
  59                }
  60        }
  61
  62        writel(value, ioaddr + GMAC_CONTROL);
  63
  64        /* Mask GMAC interrupts */
  65        value = GMAC_INT_DEFAULT_MASK;
  66
  67        if (hw->pmt)
  68                value &= ~GMAC_INT_DISABLE_PMT;
  69        if (hw->pcs)
  70                value &= ~GMAC_INT_DISABLE_PCS;
  71
  72        writel(value, ioaddr + GMAC_INT_MASK);
  73
  74#ifdef STMMAC_VLAN_TAG_USED
  75        /* Tag detection without filtering */
  76        writel(0x0, ioaddr + GMAC_VLAN_TAG);
  77#endif
  78}
  79
  80static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
  81{
  82        void __iomem *ioaddr = hw->pcsr;
  83        u32 value = readl(ioaddr + GMAC_CONTROL);
  84
  85        if (hw->rx_csum)
  86                value |= GMAC_CONTROL_IPC;
  87        else
  88                value &= ~GMAC_CONTROL_IPC;
  89
  90        writel(value, ioaddr + GMAC_CONTROL);
  91
  92        value = readl(ioaddr + GMAC_CONTROL);
  93
  94        return !!(value & GMAC_CONTROL_IPC);
  95}
  96
  97static void dwmac1000_dump_regs(struct mac_device_info *hw, u32 *reg_space)
  98{
  99        void __iomem *ioaddr = hw->pcsr;
 100        int i;
 101
 102        for (i = 0; i < 55; i++)
 103                reg_space[i] = readl(ioaddr + i * 4);
 104}
 105
 106static void dwmac1000_set_umac_addr(struct mac_device_info *hw,
 107                                    unsigned char *addr,
 108                                    unsigned int reg_n)
 109{
 110        void __iomem *ioaddr = hw->pcsr;
 111        stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
 112                            GMAC_ADDR_LOW(reg_n));
 113}
 114
 115static void dwmac1000_get_umac_addr(struct mac_device_info *hw,
 116                                    unsigned char *addr,
 117                                    unsigned int reg_n)
 118{
 119        void __iomem *ioaddr = hw->pcsr;
 120        stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
 121                            GMAC_ADDR_LOW(reg_n));
 122}
 123
 124static void dwmac1000_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits,
 125                                 int mcbitslog2)
 126{
 127        int numhashregs, regs;
 128
 129        switch (mcbitslog2) {
 130        case 6:
 131                writel(mcfilterbits[0], ioaddr + GMAC_HASH_LOW);
 132                writel(mcfilterbits[1], ioaddr + GMAC_HASH_HIGH);
 133                return;
 134                break;
 135        case 7:
 136                numhashregs = 4;
 137                break;
 138        case 8:
 139                numhashregs = 8;
 140                break;
 141        default:
 142                pr_debug("STMMAC: err in setting multicast filter\n");
 143                return;
 144                break;
 145        }
 146        for (regs = 0; regs < numhashregs; regs++)
 147                writel(mcfilterbits[regs],
 148                       ioaddr + GMAC_EXTHASH_BASE + regs * 4);
 149}
 150
 151static void dwmac1000_set_filter(struct mac_device_info *hw,
 152                                 struct net_device *dev)
 153{
 154        void __iomem *ioaddr = (void __iomem *)dev->base_addr;
 155        unsigned int value = 0;
 156        unsigned int perfect_addr_number = hw->unicast_filter_entries;
 157        u32 mc_filter[8];
 158        int mcbitslog2 = hw->mcast_bits_log2;
 159
 160        pr_debug("%s: # mcasts %d, # unicast %d\n", __func__,
 161                 netdev_mc_count(dev), netdev_uc_count(dev));
 162
 163        memset(mc_filter, 0, sizeof(mc_filter));
 164
 165        if (dev->flags & IFF_PROMISC) {
 166                value = GMAC_FRAME_FILTER_PR;
 167        } else if (dev->flags & IFF_ALLMULTI) {
 168                value = GMAC_FRAME_FILTER_PM;   /* pass all multi */
 169        } else if (!netdev_mc_empty(dev)) {
 170                struct netdev_hw_addr *ha;
 171
 172                /* Hash filter for multicast */
 173                value = GMAC_FRAME_FILTER_HMC;
 174
 175                netdev_for_each_mc_addr(ha, dev) {
 176                        /* The upper n bits of the calculated CRC are used to
 177                         * index the contents of the hash table. The number of
 178                         * bits used depends on the hardware configuration
 179                         * selected at core configuration time.
 180                         */
 181                        int bit_nr = bitrev32(~crc32_le(~0, ha->addr,
 182                                              ETH_ALEN)) >>
 183                                              (32 - mcbitslog2);
 184                        /* The most significant bit determines the register to
 185                         * use (H/L) while the other 5 bits determine the bit
 186                         * within the register.
 187                         */
 188                        mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
 189                }
 190        }
 191
 192        dwmac1000_set_mchash(ioaddr, mc_filter, mcbitslog2);
 193
 194        /* Handle multiple unicast addresses (perfect filtering) */
 195        if (netdev_uc_count(dev) > perfect_addr_number)
 196                /* Switch to promiscuous mode if more than unicast
 197                 * addresses are requested than supported by hardware.
 198                 */
 199                value |= GMAC_FRAME_FILTER_PR;
 200        else {
 201                int reg = 1;
 202                struct netdev_hw_addr *ha;
 203
 204                netdev_for_each_uc_addr(ha, dev) {
 205                        stmmac_set_mac_addr(ioaddr, ha->addr,
 206                                            GMAC_ADDR_HIGH(reg),
 207                                            GMAC_ADDR_LOW(reg));
 208                        reg++;
 209                }
 210        }
 211
 212#ifdef FRAME_FILTER_DEBUG
 213        /* Enable Receive all mode (to debug filtering_fail errors) */
 214        value |= GMAC_FRAME_FILTER_RA;
 215#endif
 216        writel(value, ioaddr + GMAC_FRAME_FILTER);
 217}
 218
 219
 220static void dwmac1000_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
 221                                unsigned int fc, unsigned int pause_time,
 222                                u32 tx_cnt)
 223{
 224        void __iomem *ioaddr = hw->pcsr;
 225        /* Set flow such that DZPQ in Mac Register 6 is 0,
 226         * and unicast pause detect is enabled.
 227         */
 228        unsigned int flow = GMAC_FLOW_CTRL_UP;
 229
 230        pr_debug("GMAC Flow-Control:\n");
 231        if (fc & FLOW_RX) {
 232                pr_debug("\tReceive Flow-Control ON\n");
 233                flow |= GMAC_FLOW_CTRL_RFE;
 234        }
 235        if (fc & FLOW_TX) {
 236                pr_debug("\tTransmit Flow-Control ON\n");
 237                flow |= GMAC_FLOW_CTRL_TFE;
 238        }
 239
 240        if (duplex) {
 241                pr_debug("\tduplex mode: PAUSE %d\n", pause_time);
 242                flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT);
 243        }
 244
 245        writel(flow, ioaddr + GMAC_FLOW_CTRL);
 246}
 247
 248static void dwmac1000_pmt(struct mac_device_info *hw, unsigned long mode)
 249{
 250        void __iomem *ioaddr = hw->pcsr;
 251        unsigned int pmt = 0;
 252
 253        if (mode & WAKE_MAGIC) {
 254                pr_debug("GMAC: WOL Magic frame\n");
 255                pmt |= power_down | magic_pkt_en;
 256        }
 257        if (mode & WAKE_UCAST) {
 258                pr_debug("GMAC: WOL on global unicast\n");
 259                pmt |= power_down | global_unicast | wake_up_frame_en;
 260        }
 261
 262        writel(pmt, ioaddr + GMAC_PMT);
 263}
 264
 265/* RGMII or SMII interface */
 266static void dwmac1000_rgsmii(void __iomem *ioaddr, struct stmmac_extra_stats *x)
 267{
 268        u32 status;
 269
 270        status = readl(ioaddr + GMAC_RGSMIIIS);
 271        x->irq_rgmii_n++;
 272
 273        /* Check the link status */
 274        if (status & GMAC_RGSMIIIS_LNKSTS) {
 275                int speed_value;
 276
 277                x->pcs_link = 1;
 278
 279                speed_value = ((status & GMAC_RGSMIIIS_SPEED) >>
 280                               GMAC_RGSMIIIS_SPEED_SHIFT);
 281                if (speed_value == GMAC_RGSMIIIS_SPEED_125)
 282                        x->pcs_speed = SPEED_1000;
 283                else if (speed_value == GMAC_RGSMIIIS_SPEED_25)
 284                        x->pcs_speed = SPEED_100;
 285                else
 286                        x->pcs_speed = SPEED_10;
 287
 288                x->pcs_duplex = (status & GMAC_RGSMIIIS_LNKMOD_MASK);
 289
 290                pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed,
 291                        x->pcs_duplex ? "Full" : "Half");
 292        } else {
 293                x->pcs_link = 0;
 294                pr_info("Link is Down\n");
 295        }
 296}
 297
 298static int dwmac1000_irq_status(struct mac_device_info *hw,
 299                                struct stmmac_extra_stats *x)
 300{
 301        void __iomem *ioaddr = hw->pcsr;
 302        u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
 303        u32 intr_mask = readl(ioaddr + GMAC_INT_MASK);
 304        int ret = 0;
 305
 306        /* Discard masked bits */
 307        intr_status &= ~intr_mask;
 308
 309        /* Not used events (e.g. MMC interrupts) are not handled. */
 310        if ((intr_status & GMAC_INT_STATUS_MMCTIS))
 311                x->mmc_tx_irq_n++;
 312        if (unlikely(intr_status & GMAC_INT_STATUS_MMCRIS))
 313                x->mmc_rx_irq_n++;
 314        if (unlikely(intr_status & GMAC_INT_STATUS_MMCCSUM))
 315                x->mmc_rx_csum_offload_irq_n++;
 316        if (unlikely(intr_status & GMAC_INT_DISABLE_PMT)) {
 317                /* clear the PMT bits 5 and 6 by reading the PMT status reg */
 318                readl(ioaddr + GMAC_PMT);
 319                x->irq_receive_pmt_irq_n++;
 320        }
 321
 322        /* MAC tx/rx EEE LPI entry/exit interrupts */
 323        if (intr_status & GMAC_INT_STATUS_LPIIS) {
 324                /* Clean LPI interrupt by reading the Reg 12 */
 325                ret = readl(ioaddr + LPI_CTRL_STATUS);
 326
 327                if (ret & LPI_CTRL_STATUS_TLPIEN)
 328                        x->irq_tx_path_in_lpi_mode_n++;
 329                if (ret & LPI_CTRL_STATUS_TLPIEX)
 330                        x->irq_tx_path_exit_lpi_mode_n++;
 331                if (ret & LPI_CTRL_STATUS_RLPIEN)
 332                        x->irq_rx_path_in_lpi_mode_n++;
 333                if (ret & LPI_CTRL_STATUS_RLPIEX)
 334                        x->irq_rx_path_exit_lpi_mode_n++;
 335        }
 336
 337        dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x);
 338
 339        if (intr_status & PCS_RGSMIIIS_IRQ)
 340                dwmac1000_rgsmii(ioaddr, x);
 341
 342        return ret;
 343}
 344
 345static void dwmac1000_set_eee_mode(struct mac_device_info *hw,
 346                                   bool en_tx_lpi_clockgating)
 347{
 348        void __iomem *ioaddr = hw->pcsr;
 349        u32 value;
 350
 351        /*TODO - en_tx_lpi_clockgating treatment */
 352
 353        /* Enable the link status receive on RGMII, SGMII ore SMII
 354         * receive path and instruct the transmit to enter in LPI
 355         * state.
 356         */
 357        value = readl(ioaddr + LPI_CTRL_STATUS);
 358        value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
 359        writel(value, ioaddr + LPI_CTRL_STATUS);
 360}
 361
 362static void dwmac1000_reset_eee_mode(struct mac_device_info *hw)
 363{
 364        void __iomem *ioaddr = hw->pcsr;
 365        u32 value;
 366
 367        value = readl(ioaddr + LPI_CTRL_STATUS);
 368        value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
 369        writel(value, ioaddr + LPI_CTRL_STATUS);
 370}
 371
 372static void dwmac1000_set_eee_pls(struct mac_device_info *hw, int link)
 373{
 374        void __iomem *ioaddr = hw->pcsr;
 375        u32 value;
 376
 377        value = readl(ioaddr + LPI_CTRL_STATUS);
 378
 379        if (link)
 380                value |= LPI_CTRL_STATUS_PLS;
 381        else
 382                value &= ~LPI_CTRL_STATUS_PLS;
 383
 384        writel(value, ioaddr + LPI_CTRL_STATUS);
 385}
 386
 387static void dwmac1000_set_eee_timer(struct mac_device_info *hw, int ls, int tw)
 388{
 389        void __iomem *ioaddr = hw->pcsr;
 390        int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);
 391
 392        /* Program the timers in the LPI timer control register:
 393         * LS: minimum time (ms) for which the link
 394         *  status from PHY should be ok before transmitting
 395         *  the LPI pattern.
 396         * TW: minimum time (us) for which the core waits
 397         *  after it has stopped transmitting the LPI pattern.
 398         */
 399        writel(value, ioaddr + LPI_TIMER_CTRL);
 400}
 401
 402static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral,
 403                               bool loopback)
 404{
 405        dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback);
 406}
 407
 408static void dwmac1000_rane(void __iomem *ioaddr, bool restart)
 409{
 410        dwmac_rane(ioaddr, GMAC_PCS_BASE, restart);
 411}
 412
 413static void dwmac1000_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv)
 414{
 415        dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv);
 416}
 417
 418static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x,
 419                            u32 rx_queues, u32 tx_queues)
 420{
 421        u32 value = readl(ioaddr + GMAC_DEBUG);
 422
 423        if (value & GMAC_DEBUG_TXSTSFSTS)
 424                x->mtl_tx_status_fifo_full++;
 425        if (value & GMAC_DEBUG_TXFSTS)
 426                x->mtl_tx_fifo_not_empty++;
 427        if (value & GMAC_DEBUG_TWCSTS)
 428                x->mmtl_fifo_ctrl++;
 429        if (value & GMAC_DEBUG_TRCSTS_MASK) {
 430                u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK)
 431                             >> GMAC_DEBUG_TRCSTS_SHIFT;
 432                if (trcsts == GMAC_DEBUG_TRCSTS_WRITE)
 433                        x->mtl_tx_fifo_read_ctrl_write++;
 434                else if (trcsts == GMAC_DEBUG_TRCSTS_TXW)
 435                        x->mtl_tx_fifo_read_ctrl_wait++;
 436                else if (trcsts == GMAC_DEBUG_TRCSTS_READ)
 437                        x->mtl_tx_fifo_read_ctrl_read++;
 438                else
 439                        x->mtl_tx_fifo_read_ctrl_idle++;
 440        }
 441        if (value & GMAC_DEBUG_TXPAUSED)
 442                x->mac_tx_in_pause++;
 443        if (value & GMAC_DEBUG_TFCSTS_MASK) {
 444                u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK)
 445                              >> GMAC_DEBUG_TFCSTS_SHIFT;
 446
 447                if (tfcsts == GMAC_DEBUG_TFCSTS_XFER)
 448                        x->mac_tx_frame_ctrl_xfer++;
 449                else if (tfcsts == GMAC_DEBUG_TFCSTS_GEN_PAUSE)
 450                        x->mac_tx_frame_ctrl_pause++;
 451                else if (tfcsts == GMAC_DEBUG_TFCSTS_WAIT)
 452                        x->mac_tx_frame_ctrl_wait++;
 453                else
 454                        x->mac_tx_frame_ctrl_idle++;
 455        }
 456        if (value & GMAC_DEBUG_TPESTS)
 457                x->mac_gmii_tx_proto_engine++;
 458        if (value & GMAC_DEBUG_RXFSTS_MASK) {
 459                u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK)
 460                             >> GMAC_DEBUG_RRCSTS_SHIFT;
 461
 462                if (rxfsts == GMAC_DEBUG_RXFSTS_FULL)
 463                        x->mtl_rx_fifo_fill_level_full++;
 464                else if (rxfsts == GMAC_DEBUG_RXFSTS_AT)
 465                        x->mtl_rx_fifo_fill_above_thresh++;
 466                else if (rxfsts == GMAC_DEBUG_RXFSTS_BT)
 467                        x->mtl_rx_fifo_fill_below_thresh++;
 468                else
 469                        x->mtl_rx_fifo_fill_level_empty++;
 470        }
 471        if (value & GMAC_DEBUG_RRCSTS_MASK) {
 472                u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >>
 473                             GMAC_DEBUG_RRCSTS_SHIFT;
 474
 475                if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH)
 476                        x->mtl_rx_fifo_read_ctrl_flush++;
 477                else if (rrcsts == GMAC_DEBUG_RRCSTS_RSTAT)
 478                        x->mtl_rx_fifo_read_ctrl_read_data++;
 479                else if (rrcsts == GMAC_DEBUG_RRCSTS_RDATA)
 480                        x->mtl_rx_fifo_read_ctrl_status++;
 481                else
 482                        x->mtl_rx_fifo_read_ctrl_idle++;
 483        }
 484        if (value & GMAC_DEBUG_RWCSTS)
 485                x->mtl_rx_fifo_ctrl_active++;
 486        if (value & GMAC_DEBUG_RFCFCSTS_MASK)
 487                x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK)
 488                                            >> GMAC_DEBUG_RFCFCSTS_SHIFT;
 489        if (value & GMAC_DEBUG_RPESTS)
 490                x->mac_gmii_rx_proto_engine++;
 491}
 492
 493static const struct stmmac_ops dwmac1000_ops = {
 494        .core_init = dwmac1000_core_init,
 495        .set_mac = stmmac_set_mac,
 496        .rx_ipc = dwmac1000_rx_ipc_enable,
 497        .dump_regs = dwmac1000_dump_regs,
 498        .host_irq_status = dwmac1000_irq_status,
 499        .set_filter = dwmac1000_set_filter,
 500        .flow_ctrl = dwmac1000_flow_ctrl,
 501        .pmt = dwmac1000_pmt,
 502        .set_umac_addr = dwmac1000_set_umac_addr,
 503        .get_umac_addr = dwmac1000_get_umac_addr,
 504        .set_eee_mode = dwmac1000_set_eee_mode,
 505        .reset_eee_mode = dwmac1000_reset_eee_mode,
 506        .set_eee_timer = dwmac1000_set_eee_timer,
 507        .set_eee_pls = dwmac1000_set_eee_pls,
 508        .debug = dwmac1000_debug,
 509        .pcs_ctrl_ane = dwmac1000_ctrl_ane,
 510        .pcs_rane = dwmac1000_rane,
 511        .pcs_get_adv_lp = dwmac1000_get_adv_lp,
 512};
 513
 514struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins,
 515                                        int perfect_uc_entries,
 516                                        int *synopsys_id)
 517{
 518        struct mac_device_info *mac;
 519        u32 hwid = readl(ioaddr + GMAC_VERSION);
 520
 521        mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
 522        if (!mac)
 523                return NULL;
 524
 525        mac->pcsr = ioaddr;
 526        mac->multicast_filter_bins = mcbins;
 527        mac->unicast_filter_entries = perfect_uc_entries;
 528        mac->mcast_bits_log2 = 0;
 529
 530        if (mac->multicast_filter_bins)
 531                mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins);
 532
 533        mac->mac = &dwmac1000_ops;
 534        mac->dma = &dwmac1000_dma_ops;
 535
 536        mac->link.duplex = GMAC_CONTROL_DM;
 537        mac->link.speed10 = GMAC_CONTROL_PS;
 538        mac->link.speed100 = GMAC_CONTROL_PS | GMAC_CONTROL_FES;
 539        mac->link.speed1000 = 0;
 540        mac->link.speed_mask = GMAC_CONTROL_PS | GMAC_CONTROL_FES;
 541        mac->mii.addr = GMAC_MII_ADDR;
 542        mac->mii.data = GMAC_MII_DATA;
 543        mac->mii.addr_shift = 11;
 544        mac->mii.addr_mask = 0x0000F800;
 545        mac->mii.reg_shift = 6;
 546        mac->mii.reg_mask = 0x000007C0;
 547        mac->mii.clk_csr_shift = 2;
 548        mac->mii.clk_csr_mask = GENMASK(5, 2);
 549
 550        /* Get and dump the chip ID */
 551        *synopsys_id = stmmac_get_synopsys_id(hwid);
 552
 553        return mac;
 554}
 555