linux/drivers/net/ethernet/ti/cpsw_ale.c
<<
>>
Prefs
   1/*
   2 * Texas Instruments 3-Port Ethernet Switch Address Lookup Engine
   3 *
   4 * Copyright (C) 2012 Texas Instruments
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation version 2.
   9 *
  10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11 * kind, whether express or implied; without even the implied warranty
  12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15#include <linux/kernel.h>
  16#include <linux/platform_device.h>
  17#include <linux/seq_file.h>
  18#include <linux/slab.h>
  19#include <linux/err.h>
  20#include <linux/io.h>
  21#include <linux/stat.h>
  22#include <linux/sysfs.h>
  23#include <linux/etherdevice.h>
  24
  25#include "cpsw_ale.h"
  26
  27#define BITMASK(bits)           (BIT(bits) - 1)
  28#define ALE_ENTRY_BITS          68
  29#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
  30
  31#define ALE_VERSION_MAJOR(rev)  ((rev >> 8) & 0xff)
  32#define ALE_VERSION_MINOR(rev)  (rev & 0xff)
  33
  34/* ALE Registers */
  35#define ALE_IDVER               0x00
  36#define ALE_CONTROL             0x08
  37#define ALE_PRESCALE            0x10
  38#define ALE_UNKNOWNVLAN         0x18
  39#define ALE_TABLE_CONTROL       0x20
  40#define ALE_TABLE               0x34
  41#define ALE_PORTCTL             0x40
  42
  43#define ALE_TABLE_WRITE         BIT(31)
  44
  45#define ALE_TYPE_FREE                   0
  46#define ALE_TYPE_ADDR                   1
  47#define ALE_TYPE_VLAN                   2
  48#define ALE_TYPE_VLAN_ADDR              3
  49
  50#define ALE_UCAST_PERSISTANT            0
  51#define ALE_UCAST_UNTOUCHED             1
  52#define ALE_UCAST_OUI                   2
  53#define ALE_UCAST_TOUCHED               3
  54
  55static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
  56{
  57        int idx;
  58
  59        idx    = start / 32;
  60        start -= idx * 32;
  61        idx    = 2 - idx; /* flip */
  62        return (ale_entry[idx] >> start) & BITMASK(bits);
  63}
  64
  65static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
  66                                      u32 value)
  67{
  68        int idx;
  69
  70        value &= BITMASK(bits);
  71        idx    = start / 32;
  72        start -= idx * 32;
  73        idx    = 2 - idx; /* flip */
  74        ale_entry[idx] &= ~(BITMASK(bits) << start);
  75        ale_entry[idx] |=  (value << start);
  76}
  77
  78#define DEFINE_ALE_FIELD(name, start, bits)                             \
  79static inline int cpsw_ale_get_##name(u32 *ale_entry)                   \
  80{                                                                       \
  81        return cpsw_ale_get_field(ale_entry, start, bits);              \
  82}                                                                       \
  83static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value)       \
  84{                                                                       \
  85        cpsw_ale_set_field(ale_entry, start, bits, value);              \
  86}
  87
  88DEFINE_ALE_FIELD(entry_type,            60,     2)
  89DEFINE_ALE_FIELD(vlan_id,               48,     12)
  90DEFINE_ALE_FIELD(mcast_state,           62,     2)
  91DEFINE_ALE_FIELD(port_mask,             66,     3)
  92DEFINE_ALE_FIELD(super,                 65,     1)
  93DEFINE_ALE_FIELD(ucast_type,            62,     2)
  94DEFINE_ALE_FIELD(port_num,              66,     2)
  95DEFINE_ALE_FIELD(blocked,               65,     1)
  96DEFINE_ALE_FIELD(secure,                64,     1)
  97DEFINE_ALE_FIELD(vlan_untag_force,      24,     3)
  98DEFINE_ALE_FIELD(vlan_reg_mcast,        16,     3)
  99DEFINE_ALE_FIELD(vlan_unreg_mcast,      8,      3)
 100DEFINE_ALE_FIELD(vlan_member_list,      0,      3)
 101DEFINE_ALE_FIELD(mcast,                 40,     1)
 102
 103/* The MAC address field in the ALE entry cannot be macroized as above */
 104static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
 105{
 106        int i;
 107
 108        for (i = 0; i < 6; i++)
 109                addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
 110}
 111
 112static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
 113{
 114        int i;
 115
 116        for (i = 0; i < 6; i++)
 117                cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
 118}
 119
 120static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry)
 121{
 122        int i;
 123
 124        WARN_ON(idx > ale->params.ale_entries);
 125
 126        __raw_writel(idx, ale->params.ale_regs + ALE_TABLE_CONTROL);
 127
 128        for (i = 0; i < ALE_ENTRY_WORDS; i++)
 129                ale_entry[i] = __raw_readl(ale->params.ale_regs +
 130                                           ALE_TABLE + 4 * i);
 131
 132        return idx;
 133}
 134
 135static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry)
 136{
 137        int i;
 138
 139        WARN_ON(idx > ale->params.ale_entries);
 140
 141        for (i = 0; i < ALE_ENTRY_WORDS; i++)
 142                __raw_writel(ale_entry[i], ale->params.ale_regs +
 143                             ALE_TABLE + 4 * i);
 144
 145        __raw_writel(idx | ALE_TABLE_WRITE, ale->params.ale_regs +
 146                     ALE_TABLE_CONTROL);
 147
 148        return idx;
 149}
 150
 151int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid)
 152{
 153        u32 ale_entry[ALE_ENTRY_WORDS];
 154        int type, idx;
 155
 156        for (idx = 0; idx < ale->params.ale_entries; idx++) {
 157                u8 entry_addr[6];
 158
 159                cpsw_ale_read(ale, idx, ale_entry);
 160                type = cpsw_ale_get_entry_type(ale_entry);
 161                if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
 162                        continue;
 163                if (cpsw_ale_get_vlan_id(ale_entry) != vid)
 164                        continue;
 165                cpsw_ale_get_addr(ale_entry, entry_addr);
 166                if (memcmp(entry_addr, addr, 6) == 0)
 167                        return idx;
 168        }
 169        return -ENOENT;
 170}
 171
 172int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid)
 173{
 174        u32 ale_entry[ALE_ENTRY_WORDS];
 175        int type, idx;
 176
 177        for (idx = 0; idx < ale->params.ale_entries; idx++) {
 178                cpsw_ale_read(ale, idx, ale_entry);
 179                type = cpsw_ale_get_entry_type(ale_entry);
 180                if (type != ALE_TYPE_VLAN)
 181                        continue;
 182                if (cpsw_ale_get_vlan_id(ale_entry) == vid)
 183                        return idx;
 184        }
 185        return -ENOENT;
 186}
 187
 188static int cpsw_ale_match_free(struct cpsw_ale *ale)
 189{
 190        u32 ale_entry[ALE_ENTRY_WORDS];
 191        int type, idx;
 192
 193        for (idx = 0; idx < ale->params.ale_entries; idx++) {
 194                cpsw_ale_read(ale, idx, ale_entry);
 195                type = cpsw_ale_get_entry_type(ale_entry);
 196                if (type == ALE_TYPE_FREE)
 197                        return idx;
 198        }
 199        return -ENOENT;
 200}
 201
 202static int cpsw_ale_find_ageable(struct cpsw_ale *ale)
 203{
 204        u32 ale_entry[ALE_ENTRY_WORDS];
 205        int type, idx;
 206
 207        for (idx = 0; idx < ale->params.ale_entries; idx++) {
 208                cpsw_ale_read(ale, idx, ale_entry);
 209                type = cpsw_ale_get_entry_type(ale_entry);
 210                if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
 211                        continue;
 212                if (cpsw_ale_get_mcast(ale_entry))
 213                        continue;
 214                type = cpsw_ale_get_ucast_type(ale_entry);
 215                if (type != ALE_UCAST_PERSISTANT &&
 216                    type != ALE_UCAST_OUI)
 217                        return idx;
 218        }
 219        return -ENOENT;
 220}
 221
 222static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
 223                                 int port_mask)
 224{
 225        int mask;
 226
 227        mask = cpsw_ale_get_port_mask(ale_entry);
 228        if ((mask & port_mask) == 0)
 229                return; /* ports dont intersect, not interested */
 230        mask &= ~port_mask;
 231
 232        /* free if only remaining port is host port */
 233        if (mask)
 234                cpsw_ale_set_port_mask(ale_entry, mask);
 235        else
 236                cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
 237}
 238
 239int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask)
 240{
 241        u32 ale_entry[ALE_ENTRY_WORDS];
 242        int ret, idx;
 243
 244        for (idx = 0; idx < ale->params.ale_entries; idx++) {
 245                cpsw_ale_read(ale, idx, ale_entry);
 246                ret = cpsw_ale_get_entry_type(ale_entry);
 247                if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
 248                        continue;
 249
 250                if (cpsw_ale_get_mcast(ale_entry)) {
 251                        u8 addr[6];
 252
 253                        cpsw_ale_get_addr(ale_entry, addr);
 254                        if (!is_broadcast_ether_addr(addr))
 255                                cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
 256                }
 257
 258                cpsw_ale_write(ale, idx, ale_entry);
 259        }
 260        return 0;
 261}
 262
 263static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry,
 264                                 int port_mask)
 265{
 266        int port;
 267
 268        port = cpsw_ale_get_port_num(ale_entry);
 269        if ((BIT(port) & port_mask) == 0)
 270                return; /* ports dont intersect, not interested */
 271        cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
 272}
 273
 274int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask)
 275{
 276        u32 ale_entry[ALE_ENTRY_WORDS];
 277        int ret, idx;
 278
 279        for (idx = 0; idx < ale->params.ale_entries; idx++) {
 280                cpsw_ale_read(ale, idx, ale_entry);
 281                ret = cpsw_ale_get_entry_type(ale_entry);
 282                if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
 283                        continue;
 284
 285                if (cpsw_ale_get_mcast(ale_entry))
 286                        cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
 287                else
 288                        cpsw_ale_flush_ucast(ale, ale_entry, port_mask);
 289
 290                cpsw_ale_write(ale, idx, ale_entry);
 291        }
 292        return 0;
 293}
 294
 295static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry,
 296                                                int flags, u16 vid)
 297{
 298        if (flags & ALE_VLAN) {
 299                cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR);
 300                cpsw_ale_set_vlan_id(ale_entry, vid);
 301        } else {
 302                cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
 303        }
 304}
 305
 306int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
 307                       int flags, u16 vid)
 308{
 309        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
 310        int idx;
 311
 312        cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
 313
 314        cpsw_ale_set_addr(ale_entry, addr);
 315        cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
 316        cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
 317        cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
 318        cpsw_ale_set_port_num(ale_entry, port);
 319
 320        idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
 321        if (idx < 0)
 322                idx = cpsw_ale_match_free(ale);
 323        if (idx < 0)
 324                idx = cpsw_ale_find_ageable(ale);
 325        if (idx < 0)
 326                return -ENOMEM;
 327
 328        cpsw_ale_write(ale, idx, ale_entry);
 329        return 0;
 330}
 331
 332int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
 333                       int flags, u16 vid)
 334{
 335        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
 336        int idx;
 337
 338        idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
 339        if (idx < 0)
 340                return -ENOENT;
 341
 342        cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
 343        cpsw_ale_write(ale, idx, ale_entry);
 344        return 0;
 345}
 346
 347int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
 348                       int flags, u16 vid, int mcast_state)
 349{
 350        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
 351        int idx, mask;
 352
 353        idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
 354        if (idx >= 0)
 355                cpsw_ale_read(ale, idx, ale_entry);
 356
 357        cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
 358
 359        cpsw_ale_set_addr(ale_entry, addr);
 360        cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
 361        cpsw_ale_set_mcast_state(ale_entry, mcast_state);
 362
 363        mask = cpsw_ale_get_port_mask(ale_entry);
 364        port_mask |= mask;
 365        cpsw_ale_set_port_mask(ale_entry, port_mask);
 366
 367        if (idx < 0)
 368                idx = cpsw_ale_match_free(ale);
 369        if (idx < 0)
 370                idx = cpsw_ale_find_ageable(ale);
 371        if (idx < 0)
 372                return -ENOMEM;
 373
 374        cpsw_ale_write(ale, idx, ale_entry);
 375        return 0;
 376}
 377
 378int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
 379                       int flags, u16 vid)
 380{
 381        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
 382        int idx;
 383
 384        idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
 385        if (idx < 0)
 386                return -EINVAL;
 387
 388        cpsw_ale_read(ale, idx, ale_entry);
 389
 390        if (port_mask)
 391                cpsw_ale_set_port_mask(ale_entry, port_mask);
 392        else
 393                cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
 394
 395        cpsw_ale_write(ale, idx, ale_entry);
 396        return 0;
 397}
 398
 399int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
 400                      int reg_mcast, int unreg_mcast)
 401{
 402        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
 403        int idx;
 404
 405        idx = cpsw_ale_match_vlan(ale, vid);
 406        if (idx >= 0)
 407                cpsw_ale_read(ale, idx, ale_entry);
 408
 409        cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
 410        cpsw_ale_set_vlan_id(ale_entry, vid);
 411
 412        cpsw_ale_set_vlan_untag_force(ale_entry, untag);
 413        cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast);
 414        cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
 415        cpsw_ale_set_vlan_member_list(ale_entry, port);
 416
 417        if (idx < 0)
 418                idx = cpsw_ale_match_free(ale);
 419        if (idx < 0)
 420                idx = cpsw_ale_find_ageable(ale);
 421        if (idx < 0)
 422                return -ENOMEM;
 423
 424        cpsw_ale_write(ale, idx, ale_entry);
 425        return 0;
 426}
 427
 428int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
 429{
 430        u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
 431        int idx;
 432
 433        idx = cpsw_ale_match_vlan(ale, vid);
 434        if (idx < 0)
 435                return -ENOENT;
 436
 437        cpsw_ale_read(ale, idx, ale_entry);
 438
 439        if (port_mask)
 440                cpsw_ale_set_vlan_member_list(ale_entry, port_mask);
 441        else
 442                cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
 443
 444        cpsw_ale_write(ale, idx, ale_entry);
 445        return 0;
 446}
 447
 448struct ale_control_info {
 449        const char      *name;
 450        int             offset, port_offset;
 451        int             shift, port_shift;
 452        int             bits;
 453};
 454
 455static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
 456        [ALE_ENABLE]            = {
 457                .name           = "enable",
 458                .offset         = ALE_CONTROL,
 459                .port_offset    = 0,
 460                .shift          = 31,
 461                .port_shift     = 0,
 462                .bits           = 1,
 463        },
 464        [ALE_CLEAR]             = {
 465                .name           = "clear",
 466                .offset         = ALE_CONTROL,
 467                .port_offset    = 0,
 468                .shift          = 30,
 469                .port_shift     = 0,
 470                .bits           = 1,
 471        },
 472        [ALE_AGEOUT]            = {
 473                .name           = "ageout",
 474                .offset         = ALE_CONTROL,
 475                .port_offset    = 0,
 476                .shift          = 29,
 477                .port_shift     = 0,
 478                .bits           = 1,
 479        },
 480        [ALE_VLAN_NOLEARN]      = {
 481                .name           = "vlan_nolearn",
 482                .offset         = ALE_CONTROL,
 483                .port_offset    = 0,
 484                .shift          = 7,
 485                .port_shift     = 0,
 486                .bits           = 1,
 487        },
 488        [ALE_NO_PORT_VLAN]      = {
 489                .name           = "no_port_vlan",
 490                .offset         = ALE_CONTROL,
 491                .port_offset    = 0,
 492                .shift          = 6,
 493                .port_shift     = 0,
 494                .bits           = 1,
 495        },
 496        [ALE_OUI_DENY]          = {
 497                .name           = "oui_deny",
 498                .offset         = ALE_CONTROL,
 499                .port_offset    = 0,
 500                .shift          = 5,
 501                .port_shift     = 0,
 502                .bits           = 1,
 503        },
 504        [ALE_BYPASS]            = {
 505                .name           = "bypass",
 506                .offset         = ALE_CONTROL,
 507                .port_offset    = 0,
 508                .shift          = 4,
 509                .port_shift     = 0,
 510                .bits           = 1,
 511        },
 512        [ALE_RATE_LIMIT_TX]     = {
 513                .name           = "rate_limit_tx",
 514                .offset         = ALE_CONTROL,
 515                .port_offset    = 0,
 516                .shift          = 3,
 517                .port_shift     = 0,
 518                .bits           = 1,
 519        },
 520        [ALE_VLAN_AWARE]        = {
 521                .name           = "vlan_aware",
 522                .offset         = ALE_CONTROL,
 523                .port_offset    = 0,
 524                .shift          = 2,
 525                .port_shift     = 0,
 526                .bits           = 1,
 527        },
 528        [ALE_AUTH_ENABLE]       = {
 529                .name           = "auth_enable",
 530                .offset         = ALE_CONTROL,
 531                .port_offset    = 0,
 532                .shift          = 1,
 533                .port_shift     = 0,
 534                .bits           = 1,
 535        },
 536        [ALE_RATE_LIMIT]        = {
 537                .name           = "rate_limit",
 538                .offset         = ALE_CONTROL,
 539                .port_offset    = 0,
 540                .shift          = 0,
 541                .port_shift     = 0,
 542                .bits           = 1,
 543        },
 544        [ALE_PORT_STATE]        = {
 545                .name           = "port_state",
 546                .offset         = ALE_PORTCTL,
 547                .port_offset    = 4,
 548                .shift          = 0,
 549                .port_shift     = 0,
 550                .bits           = 2,
 551        },
 552        [ALE_PORT_DROP_UNTAGGED] = {
 553                .name           = "drop_untagged",
 554                .offset         = ALE_PORTCTL,
 555                .port_offset    = 4,
 556                .shift          = 2,
 557                .port_shift     = 0,
 558                .bits           = 1,
 559        },
 560        [ALE_PORT_DROP_UNKNOWN_VLAN] = {
 561                .name           = "drop_unknown",
 562                .offset         = ALE_PORTCTL,
 563                .port_offset    = 4,
 564                .shift          = 3,
 565                .port_shift     = 0,
 566                .bits           = 1,
 567        },
 568        [ALE_PORT_NOLEARN]      = {
 569                .name           = "nolearn",
 570                .offset         = ALE_PORTCTL,
 571                .port_offset    = 4,
 572                .shift          = 4,
 573                .port_shift     = 0,
 574                .bits           = 1,
 575        },
 576        [ALE_PORT_MCAST_LIMIT]  = {
 577                .name           = "mcast_limit",
 578                .offset         = ALE_PORTCTL,
 579                .port_offset    = 4,
 580                .shift          = 16,
 581                .port_shift     = 0,
 582                .bits           = 8,
 583        },
 584        [ALE_PORT_BCAST_LIMIT]  = {
 585                .name           = "bcast_limit",
 586                .offset         = ALE_PORTCTL,
 587                .port_offset    = 4,
 588                .shift          = 24,
 589                .port_shift     = 0,
 590                .bits           = 8,
 591        },
 592        [ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
 593                .name           = "unknown_vlan_member",
 594                .offset         = ALE_UNKNOWNVLAN,
 595                .port_offset    = 0,
 596                .shift          = 0,
 597                .port_shift     = 0,
 598                .bits           = 6,
 599        },
 600        [ALE_PORT_UNKNOWN_MCAST_FLOOD] = {
 601                .name           = "unknown_mcast_flood",
 602                .offset         = ALE_UNKNOWNVLAN,
 603                .port_offset    = 0,
 604                .shift          = 8,
 605                .port_shift     = 0,
 606                .bits           = 6,
 607        },
 608        [ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = {
 609                .name           = "unknown_reg_flood",
 610                .offset         = ALE_UNKNOWNVLAN,
 611                .port_offset    = 0,
 612                .shift          = 16,
 613                .port_shift     = 0,
 614                .bits           = 6,
 615        },
 616        [ALE_PORT_UNTAGGED_EGRESS] = {
 617                .name           = "untagged_egress",
 618                .offset         = ALE_UNKNOWNVLAN,
 619                .port_offset    = 0,
 620                .shift          = 24,
 621                .port_shift     = 0,
 622                .bits           = 6,
 623        },
 624};
 625
 626int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
 627                         int value)
 628{
 629        const struct ale_control_info *info;
 630        int offset, shift;
 631        u32 tmp, mask;
 632
 633        if (control < 0 || control >= ARRAY_SIZE(ale_controls))
 634                return -EINVAL;
 635
 636        info = &ale_controls[control];
 637        if (info->port_offset == 0 && info->port_shift == 0)
 638                port = 0; /* global, port is a dont care */
 639
 640        if (port < 0 || port > ale->params.ale_ports)
 641                return -EINVAL;
 642
 643        mask = BITMASK(info->bits);
 644        if (value & ~mask)
 645                return -EINVAL;
 646
 647        offset = info->offset + (port * info->port_offset);
 648        shift  = info->shift  + (port * info->port_shift);
 649
 650        tmp = __raw_readl(ale->params.ale_regs + offset);
 651        tmp = (tmp & ~(mask << shift)) | (value << shift);
 652        __raw_writel(tmp, ale->params.ale_regs + offset);
 653
 654        return 0;
 655}
 656
 657int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
 658{
 659        const struct ale_control_info *info;
 660        int offset, shift;
 661        u32 tmp;
 662
 663        if (control < 0 || control >= ARRAY_SIZE(ale_controls))
 664                return -EINVAL;
 665
 666        info = &ale_controls[control];
 667        if (info->port_offset == 0 && info->port_shift == 0)
 668                port = 0; /* global, port is a dont care */
 669
 670        if (port < 0 || port > ale->params.ale_ports)
 671                return -EINVAL;
 672
 673        offset = info->offset + (port * info->port_offset);
 674        shift  = info->shift  + (port * info->port_shift);
 675
 676        tmp = __raw_readl(ale->params.ale_regs + offset) >> shift;
 677        return tmp & BITMASK(info->bits);
 678}
 679
 680static void cpsw_ale_timer(unsigned long arg)
 681{
 682        struct cpsw_ale *ale = (struct cpsw_ale *)arg;
 683
 684        cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
 685
 686        if (ale->ageout) {
 687                ale->timer.expires = jiffies + ale->ageout;
 688                add_timer(&ale->timer);
 689        }
 690}
 691
 692int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout)
 693{
 694        del_timer_sync(&ale->timer);
 695        ale->ageout = ageout * HZ;
 696        if (ale->ageout) {
 697                ale->timer.expires = jiffies + ale->ageout;
 698                add_timer(&ale->timer);
 699        }
 700        return 0;
 701}
 702
 703void cpsw_ale_start(struct cpsw_ale *ale)
 704{
 705        u32 rev;
 706
 707        rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
 708        dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
 709                ALE_VERSION_MAJOR(rev), ALE_VERSION_MINOR(rev));
 710        cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
 711        cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
 712
 713        init_timer(&ale->timer);
 714        ale->timer.data     = (unsigned long)ale;
 715        ale->timer.function = cpsw_ale_timer;
 716        if (ale->ageout) {
 717                ale->timer.expires = jiffies + ale->ageout;
 718                add_timer(&ale->timer);
 719        }
 720}
 721
 722void cpsw_ale_stop(struct cpsw_ale *ale)
 723{
 724        del_timer_sync(&ale->timer);
 725}
 726
 727struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
 728{
 729        struct cpsw_ale *ale;
 730
 731        ale = kzalloc(sizeof(*ale), GFP_KERNEL);
 732        if (!ale)
 733                return NULL;
 734
 735        ale->params = *params;
 736        ale->ageout = ale->params.ale_ageout * HZ;
 737
 738        return ale;
 739}
 740
 741int cpsw_ale_destroy(struct cpsw_ale *ale)
 742{
 743        if (!ale)
 744                return -EINVAL;
 745        cpsw_ale_stop(ale);
 746        cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
 747        kfree(ale);
 748        return 0;
 749}
 750