Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /* Microchip Sparx5 Switch driver
0003  *
0004  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
0005  */
0006 
0007 #include <net/switchdev.h>
0008 #include <linux/if_bridge.h>
0009 #include <linux/iopoll.h>
0010 
0011 #include "sparx5_main_regs.h"
0012 #include "sparx5_main.h"
0013 
0014 /* Commands for Mac Table Command register */
0015 #define MAC_CMD_LEARN         0 /* Insert (Learn) 1 entry */
0016 #define MAC_CMD_UNLEARN       1 /* Unlearn (Forget) 1 entry */
0017 #define MAC_CMD_LOOKUP        2 /* Look up 1 entry */
0018 #define MAC_CMD_READ          3 /* Read entry at Mac Table Index */
0019 #define MAC_CMD_WRITE         4 /* Write entry at Mac Table Index */
0020 #define MAC_CMD_SCAN          5 /* Scan (Age or find next) */
0021 #define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */
0022 #define MAC_CMD_CLEAR_ALL     7 /* Delete all entries in table */
0023 
0024 /* Commands for MAC_ENTRY_ADDR_TYPE */
0025 #define  MAC_ENTRY_ADDR_TYPE_UPSID_PN         0
0026 #define  MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
0027 #define  MAC_ENTRY_ADDR_TYPE_GLAG             2
0028 #define  MAC_ENTRY_ADDR_TYPE_MC_IDX           3
0029 
0030 #define TABLE_UPDATE_SLEEP_US 10
0031 #define TABLE_UPDATE_TIMEOUT_US 100000
0032 
0033 struct sparx5_mact_entry {
0034     struct list_head list;
0035     unsigned char mac[ETH_ALEN];
0036     u32 flags;
0037 #define MAC_ENT_ALIVE   BIT(0)
0038 #define MAC_ENT_MOVED   BIT(1)
0039 #define MAC_ENT_LOCK    BIT(2)
0040     u16 vid;
0041     u16 port;
0042 };
0043 
0044 static int sparx5_mact_get_status(struct sparx5 *sparx5)
0045 {
0046     return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
0047 }
0048 
0049 static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
0050 {
0051     u32 val;
0052 
0053     return readx_poll_timeout(sparx5_mact_get_status,
0054         sparx5, val,
0055         LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
0056         TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
0057 }
0058 
0059 static void sparx5_mact_select(struct sparx5 *sparx5,
0060                    const unsigned char mac[ETH_ALEN],
0061                    u16 vid)
0062 {
0063     u32 macl = 0, mach = 0;
0064 
0065     /* Set the MAC address to handle and the vlan associated in a format
0066      * understood by the hardware.
0067      */
0068     mach |= vid    << 16;
0069     mach |= mac[0] << 8;
0070     mach |= mac[1] << 0;
0071     macl |= mac[2] << 24;
0072     macl |= mac[3] << 16;
0073     macl |= mac[4] << 8;
0074     macl |= mac[5] << 0;
0075 
0076     spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
0077     spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
0078 }
0079 
0080 int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
0081               const unsigned char mac[ETH_ALEN], u16 vid)
0082 {
0083     int addr, type, ret;
0084 
0085     if (pgid < SPX5_PORTS) {
0086         type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
0087         addr = pgid % 32;
0088         addr += (pgid / 32) << 5; /* Add upsid */
0089     } else {
0090         type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
0091         addr = pgid - SPX5_PORTS;
0092     }
0093 
0094     mutex_lock(&sparx5->lock);
0095 
0096     sparx5_mact_select(sparx5, mac, vid);
0097 
0098     /* MAC entry properties */
0099     spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
0100         LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
0101         LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
0102         LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
0103         sparx5, LRN_MAC_ACCESS_CFG_2);
0104     spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
0105 
0106     /*  Insert/learn new entry */
0107     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
0108         LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0109         sparx5, LRN_COMMON_ACCESS_CTRL);
0110 
0111     ret = sparx5_mact_wait_for_completion(sparx5);
0112 
0113     mutex_unlock(&sparx5->lock);
0114 
0115     return ret;
0116 }
0117 
0118 int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
0119 {
0120     struct sparx5_port *port = netdev_priv(dev);
0121     struct sparx5 *sparx5 = port->sparx5;
0122 
0123     return sparx5_mact_forget(sparx5, addr, port->pvid);
0124 }
0125 
0126 int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
0127 {
0128     struct sparx5_port *port = netdev_priv(dev);
0129     struct sparx5 *sparx5 = port->sparx5;
0130 
0131     return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
0132 }
0133 
0134 static int sparx5_mact_get(struct sparx5 *sparx5,
0135                unsigned char mac[ETH_ALEN],
0136                u16 *vid, u32 *pcfg2)
0137 {
0138     u32 mach, macl, cfg2;
0139     int ret = -ENOENT;
0140 
0141     cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
0142     if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
0143         mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
0144         macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
0145         mac[0] = ((mach >> 8)  & 0xff);
0146         mac[1] = ((mach >> 0)  & 0xff);
0147         mac[2] = ((macl >> 24) & 0xff);
0148         mac[3] = ((macl >> 16) & 0xff);
0149         mac[4] = ((macl >> 8)  & 0xff);
0150         mac[5] = ((macl >> 0)  & 0xff);
0151         *vid = mach >> 16;
0152         *pcfg2 = cfg2;
0153         ret = 0;
0154     }
0155 
0156     return ret;
0157 }
0158 
0159 bool sparx5_mact_getnext(struct sparx5 *sparx5,
0160              unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
0161 {
0162     u32 cfg2;
0163     int ret;
0164 
0165     mutex_lock(&sparx5->lock);
0166 
0167     sparx5_mact_select(sparx5, mac, *vid);
0168 
0169     spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
0170         LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
0171         sparx5, LRN_SCAN_NEXT_CFG);
0172     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
0173         (MAC_CMD_FIND_SMALLEST) |
0174         LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0175         sparx5, LRN_COMMON_ACCESS_CTRL);
0176 
0177     ret = sparx5_mact_wait_for_completion(sparx5);
0178     if (ret == 0) {
0179         ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
0180         if (ret == 0)
0181             *pcfg2 = cfg2;
0182     }
0183 
0184     mutex_unlock(&sparx5->lock);
0185 
0186     return ret == 0;
0187 }
0188 
0189 bool sparx5_mact_find(struct sparx5 *sparx5,
0190               const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
0191 {
0192     int ret;
0193     u32 cfg2;
0194 
0195     mutex_lock(&sparx5->lock);
0196 
0197     sparx5_mact_select(sparx5, mac, vid);
0198 
0199     /* Issue a lookup command */
0200     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
0201         LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0202         sparx5, LRN_COMMON_ACCESS_CTRL);
0203 
0204     ret = sparx5_mact_wait_for_completion(sparx5);
0205     if (ret == 0) {
0206         cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
0207         if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))
0208             *pcfg2 = cfg2;
0209         else
0210             ret = -ENOENT;
0211     }
0212 
0213     mutex_unlock(&sparx5->lock);
0214 
0215     return ret;
0216 }
0217 
0218 int sparx5_mact_forget(struct sparx5 *sparx5,
0219                const unsigned char mac[ETH_ALEN], u16 vid)
0220 {
0221     int ret;
0222 
0223     mutex_lock(&sparx5->lock);
0224 
0225     sparx5_mact_select(sparx5, mac, vid);
0226 
0227     /* Issue an unlearn command */
0228     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
0229         LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0230         sparx5, LRN_COMMON_ACCESS_CTRL);
0231 
0232     ret = sparx5_mact_wait_for_completion(sparx5);
0233 
0234     mutex_unlock(&sparx5->lock);
0235 
0236     return ret;
0237 }
0238 
0239 static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
0240                           const unsigned char *mac,
0241                           u16 vid, u16 port_index)
0242 {
0243     struct sparx5_mact_entry *mact_entry;
0244 
0245     mact_entry = devm_kzalloc(sparx5->dev,
0246                   sizeof(*mact_entry), GFP_ATOMIC);
0247     if (!mact_entry)
0248         return NULL;
0249 
0250     memcpy(mact_entry->mac, mac, ETH_ALEN);
0251     mact_entry->vid = vid;
0252     mact_entry->port = port_index;
0253     return mact_entry;
0254 }
0255 
0256 static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
0257                          const unsigned char *mac,
0258                          u16 vid, u16 port_index)
0259 {
0260     struct sparx5_mact_entry *mact_entry;
0261     struct sparx5_mact_entry *res = NULL;
0262 
0263     mutex_lock(&sparx5->mact_lock);
0264     list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
0265         if (mact_entry->vid == vid &&
0266             ether_addr_equal(mac, mact_entry->mac) &&
0267             mact_entry->port == port_index) {
0268             res = mact_entry;
0269             break;
0270         }
0271     }
0272     mutex_unlock(&sparx5->mact_lock);
0273 
0274     return res;
0275 }
0276 
0277 static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
0278                       const char *mac, u16 vid,
0279                       struct net_device *dev, bool offloaded)
0280 {
0281     struct switchdev_notifier_fdb_info info = {};
0282 
0283     info.addr = mac;
0284     info.vid = vid;
0285     info.offloaded = offloaded;
0286     call_switchdev_notifiers(type, dev, &info.info, NULL);
0287 }
0288 
0289 int sparx5_add_mact_entry(struct sparx5 *sparx5,
0290               struct net_device *dev,
0291               u16 portno,
0292               const unsigned char *addr, u16 vid)
0293 {
0294     struct sparx5_mact_entry *mact_entry;
0295     int ret;
0296     u32 cfg2;
0297 
0298     ret = sparx5_mact_find(sparx5, addr, vid, &cfg2);
0299     if (!ret)
0300         return 0;
0301 
0302     /* In case the entry already exists, don't add it again to SW,
0303      * just update HW, but we need to look in the actual HW because
0304      * it is possible for an entry to be learn by HW and before the
0305      * mact thread to start the frame will reach CPU and the CPU will
0306      * add the entry but without the extern_learn flag.
0307      */
0308     mact_entry = find_mact_entry(sparx5, addr, vid, portno);
0309     if (mact_entry)
0310         goto update_hw;
0311 
0312     /* Add the entry in SW MAC table not to get the notification when
0313      * SW is pulling again
0314      */
0315     mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);
0316     if (!mact_entry)
0317         return -ENOMEM;
0318 
0319     mutex_lock(&sparx5->mact_lock);
0320     list_add_tail(&mact_entry->list, &sparx5->mact_entries);
0321     mutex_unlock(&sparx5->mact_lock);
0322 
0323 update_hw:
0324     ret = sparx5_mact_learn(sparx5, portno, addr, vid);
0325 
0326     /* New entry? */
0327     if (mact_entry->flags == 0) {
0328         mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
0329         sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,
0330                       dev, true);
0331     }
0332 
0333     return ret;
0334 }
0335 
0336 int sparx5_del_mact_entry(struct sparx5 *sparx5,
0337               const unsigned char *addr,
0338               u16 vid)
0339 {
0340     struct sparx5_mact_entry *mact_entry, *tmp;
0341 
0342     /* Delete the entry in SW MAC table not to get the notification when
0343      * SW is pulling again
0344      */
0345     mutex_lock(&sparx5->mact_lock);
0346     list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
0347                  list) {
0348         if ((vid == 0 || mact_entry->vid == vid) &&
0349             ether_addr_equal(addr, mact_entry->mac)) {
0350             list_del(&mact_entry->list);
0351             devm_kfree(sparx5->dev, mact_entry);
0352 
0353             sparx5_mact_forget(sparx5, addr, mact_entry->vid);
0354         }
0355     }
0356     mutex_unlock(&sparx5->mact_lock);
0357 
0358     return 0;
0359 }
0360 
0361 static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
0362                      unsigned char mac[ETH_ALEN],
0363                      u16 vid, u32 cfg2)
0364 {
0365     struct sparx5_mact_entry *mact_entry;
0366     bool found = false;
0367     u16 port;
0368 
0369     if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
0370         MAC_ENTRY_ADDR_TYPE_UPSID_PN)
0371         return;
0372 
0373     port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
0374     if (port >= SPX5_PORTS)
0375         return;
0376 
0377     if (!test_bit(port, sparx5->bridge_mask))
0378         return;
0379 
0380     mutex_lock(&sparx5->mact_lock);
0381     list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
0382         if (mact_entry->vid == vid &&
0383             ether_addr_equal(mac, mact_entry->mac)) {
0384             found = true;
0385             mact_entry->flags |= MAC_ENT_ALIVE;
0386             if (mact_entry->port != port) {
0387                 dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
0388                      mact_entry->port, port);
0389                 mact_entry->port = port;
0390                 mact_entry->flags |= MAC_ENT_MOVED;
0391             }
0392             /* Entry handled */
0393             break;
0394         }
0395     }
0396     mutex_unlock(&sparx5->mact_lock);
0397 
0398     if (found && !(mact_entry->flags & MAC_ENT_MOVED))
0399         /* Present, not moved */
0400         return;
0401 
0402     if (!found) {
0403         /* Entry not found - now add */
0404         mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
0405         if (!mact_entry)
0406             return;
0407 
0408         mact_entry->flags |= MAC_ENT_ALIVE;
0409         mutex_lock(&sparx5->mact_lock);
0410         list_add_tail(&mact_entry->list, &sparx5->mact_entries);
0411         mutex_unlock(&sparx5->mact_lock);
0412     }
0413 
0414     /* New or moved entry - notify bridge */
0415     sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
0416                   mac, vid, sparx5->ports[port]->ndev,
0417                   true);
0418 }
0419 
0420 void sparx5_mact_pull_work(struct work_struct *work)
0421 {
0422     struct delayed_work *del_work = to_delayed_work(work);
0423     struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
0424                          mact_work);
0425     struct sparx5_mact_entry *mact_entry, *tmp;
0426     unsigned char mac[ETH_ALEN];
0427     u32 cfg2;
0428     u16 vid;
0429     int ret;
0430 
0431     /* Reset MAC entry flags */
0432     mutex_lock(&sparx5->mact_lock);
0433     list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
0434         mact_entry->flags &= MAC_ENT_LOCK;
0435     mutex_unlock(&sparx5->mact_lock);
0436 
0437     /* MAIN mac address processing loop */
0438     vid = 0;
0439     memset(mac, 0, sizeof(mac));
0440     do {
0441         mutex_lock(&sparx5->lock);
0442         sparx5_mact_select(sparx5, mac, vid);
0443         spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
0444             sparx5, LRN_SCAN_NEXT_CFG);
0445         spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
0446             (MAC_CMD_FIND_SMALLEST) |
0447             LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0448             sparx5, LRN_COMMON_ACCESS_CTRL);
0449         ret = sparx5_mact_wait_for_completion(sparx5);
0450         if (ret == 0)
0451             ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
0452         mutex_unlock(&sparx5->lock);
0453         if (ret == 0)
0454             sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
0455     } while (ret == 0);
0456 
0457     mutex_lock(&sparx5->mact_lock);
0458     list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
0459                  list) {
0460         /* If the entry is in HW or permanent, then skip */
0461         if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
0462             continue;
0463 
0464         sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
0465                       mact_entry->mac, mact_entry->vid,
0466                       sparx5->ports[mact_entry->port]->ndev,
0467                       true);
0468 
0469         list_del(&mact_entry->list);
0470         devm_kfree(sparx5->dev, mact_entry);
0471     }
0472     mutex_unlock(&sparx5->mact_lock);
0473 
0474     queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
0475                SPX5_MACT_PULL_DELAY);
0476 }
0477 
0478 void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
0479 {
0480     int value = max(1, msecs / 10); /* unit 10 ms */
0481 
0482     spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */
0483          LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */
0484          LRN_AUTOAGE_CFG_UNIT_SIZE |
0485          LRN_AUTOAGE_CFG_PERIOD_VAL,
0486          sparx5,
0487          LRN_AUTOAGE_CFG(0));
0488 }
0489 
0490 void sparx5_mact_init(struct sparx5 *sparx5)
0491 {
0492     mutex_init(&sparx5->lock);
0493 
0494     /*  Flush MAC table */
0495     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
0496         LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
0497         sparx5, LRN_COMMON_ACCESS_CTRL);
0498 
0499     if (sparx5_mact_wait_for_completion(sparx5) != 0)
0500         dev_warn(sparx5->dev, "MAC flush error\n");
0501 
0502     sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);
0503 }