Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 #include <net/switchdev.h>
0004 #include "lan966x_main.h"
0005 
0006 #define LAN966X_MAC_COLUMNS     4
0007 #define MACACCESS_CMD_IDLE      0
0008 #define MACACCESS_CMD_LEARN     1
0009 #define MACACCESS_CMD_FORGET        2
0010 #define MACACCESS_CMD_AGE       3
0011 #define MACACCESS_CMD_GET_NEXT      4
0012 #define MACACCESS_CMD_INIT      5
0013 #define MACACCESS_CMD_READ      6
0014 #define MACACCESS_CMD_WRITE     7
0015 #define MACACCESS_CMD_SYNC_GET_NEXT 8
0016 
0017 #define LAN966X_MAC_INVALID_ROW     -1
0018 
0019 struct lan966x_mac_entry {
0020     struct list_head list;
0021     unsigned char mac[ETH_ALEN] __aligned(2);
0022     u16 vid;
0023     u16 port_index;
0024     int row;
0025 };
0026 
0027 struct lan966x_mac_raw_entry {
0028     u32 mach;
0029     u32 macl;
0030     u32 maca;
0031     bool processed;
0032 };
0033 
0034 static int lan966x_mac_get_status(struct lan966x *lan966x)
0035 {
0036     return lan_rd(lan966x, ANA_MACACCESS);
0037 }
0038 
0039 static int lan966x_mac_wait_for_completion(struct lan966x *lan966x)
0040 {
0041     u32 val;
0042 
0043     return readx_poll_timeout_atomic(lan966x_mac_get_status,
0044                      lan966x, val,
0045                      (ANA_MACACCESS_MAC_TABLE_CMD_GET(val)) ==
0046                      MACACCESS_CMD_IDLE,
0047                      TABLE_UPDATE_SLEEP_US,
0048                      TABLE_UPDATE_TIMEOUT_US);
0049 }
0050 
0051 static void lan966x_mac_select(struct lan966x *lan966x,
0052                    const unsigned char mac[ETH_ALEN],
0053                    unsigned int vid)
0054 {
0055     u32 macl = 0, mach = 0;
0056 
0057     /* Set the MAC address to handle and the vlan associated in a format
0058      * understood by the hardware.
0059      */
0060     mach |= vid    << 16;
0061     mach |= mac[0] << 8;
0062     mach |= mac[1] << 0;
0063     macl |= mac[2] << 24;
0064     macl |= mac[3] << 16;
0065     macl |= mac[4] << 8;
0066     macl |= mac[5] << 0;
0067 
0068     lan_wr(macl, lan966x, ANA_MACLDATA);
0069     lan_wr(mach, lan966x, ANA_MACHDATA);
0070 }
0071 
0072 static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid,
0073                    bool cpu_copy,
0074                    const unsigned char mac[ETH_ALEN],
0075                    unsigned int vid,
0076                    enum macaccess_entry_type type)
0077 {
0078     int ret;
0079 
0080     spin_lock(&lan966x->mac_lock);
0081     lan966x_mac_select(lan966x, mac, vid);
0082 
0083     /* Issue a write command */
0084     lan_wr(ANA_MACACCESS_VALID_SET(1) |
0085            ANA_MACACCESS_CHANGE2SW_SET(0) |
0086            ANA_MACACCESS_MAC_CPU_COPY_SET(cpu_copy) |
0087            ANA_MACACCESS_DEST_IDX_SET(pgid) |
0088            ANA_MACACCESS_ENTRYTYPE_SET(type) |
0089            ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN),
0090            lan966x, ANA_MACACCESS);
0091 
0092     ret = lan966x_mac_wait_for_completion(lan966x);
0093     spin_unlock(&lan966x->mac_lock);
0094 
0095     return ret;
0096 }
0097 
0098 /* The mask of the front ports is encoded inside the mac parameter via a call
0099  * to lan966x_mdb_encode_mac().
0100  */
0101 int lan966x_mac_ip_learn(struct lan966x *lan966x,
0102              bool cpu_copy,
0103              const unsigned char mac[ETH_ALEN],
0104              unsigned int vid,
0105              enum macaccess_entry_type type)
0106 {
0107     WARN_ON(type != ENTRYTYPE_MACV4 && type != ENTRYTYPE_MACV6);
0108 
0109     return __lan966x_mac_learn(lan966x, 0, cpu_copy, mac, vid, type);
0110 }
0111 
0112 int lan966x_mac_learn(struct lan966x *lan966x, int port,
0113               const unsigned char mac[ETH_ALEN],
0114               unsigned int vid,
0115               enum macaccess_entry_type type)
0116 {
0117     WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED);
0118 
0119     return __lan966x_mac_learn(lan966x, port, false, mac, vid, type);
0120 }
0121 
0122 static int lan966x_mac_forget_locked(struct lan966x *lan966x,
0123                      const unsigned char mac[ETH_ALEN],
0124                      unsigned int vid,
0125                      enum macaccess_entry_type type)
0126 {
0127     lockdep_assert_held(&lan966x->mac_lock);
0128 
0129     lan966x_mac_select(lan966x, mac, vid);
0130 
0131     /* Issue a forget command */
0132     lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
0133            ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_FORGET),
0134            lan966x, ANA_MACACCESS);
0135 
0136     return lan966x_mac_wait_for_completion(lan966x);
0137 }
0138 
0139 int lan966x_mac_forget(struct lan966x *lan966x,
0140                const unsigned char mac[ETH_ALEN],
0141                unsigned int vid,
0142                enum macaccess_entry_type type)
0143 {
0144     int ret;
0145 
0146     spin_lock(&lan966x->mac_lock);
0147     ret = lan966x_mac_forget_locked(lan966x, mac, vid, type);
0148     spin_unlock(&lan966x->mac_lock);
0149 
0150     return ret;
0151 }
0152 
0153 int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid)
0154 {
0155     return lan966x_mac_learn(lan966x, PGID_CPU, addr, vid, ENTRYTYPE_LOCKED);
0156 }
0157 
0158 int lan966x_mac_cpu_forget(struct lan966x *lan966x, const char *addr, u16 vid)
0159 {
0160     return lan966x_mac_forget(lan966x, addr, vid, ENTRYTYPE_LOCKED);
0161 }
0162 
0163 void lan966x_mac_set_ageing(struct lan966x *lan966x,
0164                 u32 ageing)
0165 {
0166     lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ageing / 2),
0167         ANA_AUTOAGE_AGE_PERIOD,
0168         lan966x, ANA_AUTOAGE);
0169 }
0170 
0171 void lan966x_mac_init(struct lan966x *lan966x)
0172 {
0173     /* Clear the MAC table */
0174     lan_wr(MACACCESS_CMD_INIT, lan966x, ANA_MACACCESS);
0175     lan966x_mac_wait_for_completion(lan966x);
0176 
0177     spin_lock_init(&lan966x->mac_lock);
0178     INIT_LIST_HEAD(&lan966x->mac_entries);
0179 }
0180 
0181 static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *mac,
0182                              u16 vid, u16 port_index)
0183 {
0184     struct lan966x_mac_entry *mac_entry;
0185 
0186     mac_entry = kzalloc(sizeof(*mac_entry), GFP_ATOMIC);
0187     if (!mac_entry)
0188         return NULL;
0189 
0190     memcpy(mac_entry->mac, mac, ETH_ALEN);
0191     mac_entry->vid = vid;
0192     mac_entry->port_index = port_index;
0193     mac_entry->row = LAN966X_MAC_INVALID_ROW;
0194     return mac_entry;
0195 }
0196 
0197 static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x,
0198                             const unsigned char *mac,
0199                             u16 vid, u16 port_index)
0200 {
0201     struct lan966x_mac_entry *res = NULL;
0202     struct lan966x_mac_entry *mac_entry;
0203 
0204     list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {
0205         if (mac_entry->vid == vid &&
0206             ether_addr_equal(mac, mac_entry->mac) &&
0207             mac_entry->port_index == port_index) {
0208             res = mac_entry;
0209             break;
0210         }
0211     }
0212 
0213     return res;
0214 }
0215 
0216 static int lan966x_mac_lookup(struct lan966x *lan966x,
0217                   const unsigned char mac[ETH_ALEN],
0218                   unsigned int vid, enum macaccess_entry_type type)
0219 {
0220     int ret;
0221 
0222     lan966x_mac_select(lan966x, mac, vid);
0223 
0224     /* Issue a read command */
0225     lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
0226            ANA_MACACCESS_VALID_SET(1) |
0227            ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_READ),
0228            lan966x, ANA_MACACCESS);
0229 
0230     ret = lan966x_mac_wait_for_completion(lan966x);
0231     if (ret)
0232         return ret;
0233 
0234     return ANA_MACACCESS_VALID_GET(lan_rd(lan966x, ANA_MACACCESS));
0235 }
0236 
0237 static void lan966x_fdb_call_notifiers(enum switchdev_notifier_type type,
0238                        const char *mac, u16 vid,
0239                        struct net_device *dev)
0240 {
0241     struct switchdev_notifier_fdb_info info = { 0 };
0242 
0243     info.addr = mac;
0244     info.vid = vid;
0245     info.offloaded = true;
0246     call_switchdev_notifiers(type, dev, &info.info, NULL);
0247 }
0248 
0249 int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port,
0250               const unsigned char *addr, u16 vid)
0251 {
0252     struct lan966x_mac_entry *mac_entry;
0253 
0254     spin_lock(&lan966x->mac_lock);
0255     if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL)) {
0256         spin_unlock(&lan966x->mac_lock);
0257         return 0;
0258     }
0259 
0260     /* In case the entry already exists, don't add it again to SW,
0261      * just update HW, but we need to look in the actual HW because
0262      * it is possible for an entry to be learn by HW and before we
0263      * get the interrupt the frame will reach CPU and the CPU will
0264      * add the entry but without the extern_learn flag.
0265      */
0266     mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);
0267     if (mac_entry) {
0268         spin_unlock(&lan966x->mac_lock);
0269         goto mac_learn;
0270     }
0271 
0272     mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);
0273     if (!mac_entry) {
0274         spin_unlock(&lan966x->mac_lock);
0275         return -ENOMEM;
0276     }
0277 
0278     list_add_tail(&mac_entry->list, &lan966x->mac_entries);
0279     spin_unlock(&lan966x->mac_lock);
0280 
0281     lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);
0282 
0283 mac_learn:
0284     lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
0285 
0286     return 0;
0287 }
0288 
0289 int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr,
0290               u16 vid)
0291 {
0292     struct lan966x_mac_entry *mac_entry, *tmp;
0293 
0294     spin_lock(&lan966x->mac_lock);
0295     list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
0296                  list) {
0297         if (mac_entry->vid == vid &&
0298             ether_addr_equal(addr, mac_entry->mac)) {
0299             lan966x_mac_forget_locked(lan966x, mac_entry->mac,
0300                           mac_entry->vid,
0301                           ENTRYTYPE_LOCKED);
0302 
0303             list_del(&mac_entry->list);
0304             kfree(mac_entry);
0305         }
0306     }
0307     spin_unlock(&lan966x->mac_lock);
0308 
0309     return 0;
0310 }
0311 
0312 void lan966x_mac_purge_entries(struct lan966x *lan966x)
0313 {
0314     struct lan966x_mac_entry *mac_entry, *tmp;
0315 
0316     spin_lock(&lan966x->mac_lock);
0317     list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
0318                  list) {
0319         lan966x_mac_forget_locked(lan966x, mac_entry->mac,
0320                       mac_entry->vid, ENTRYTYPE_LOCKED);
0321 
0322         list_del(&mac_entry->list);
0323         kfree(mac_entry);
0324     }
0325     spin_unlock(&lan966x->mac_lock);
0326 }
0327 
0328 static void lan966x_mac_notifiers(enum switchdev_notifier_type type,
0329                   unsigned char *mac, u32 vid,
0330                   struct net_device *dev)
0331 {
0332     rtnl_lock();
0333     lan966x_fdb_call_notifiers(type, mac, vid, dev);
0334     rtnl_unlock();
0335 }
0336 
0337 static void lan966x_mac_process_raw_entry(struct lan966x_mac_raw_entry *raw_entry,
0338                       u8 *mac, u16 *vid, u32 *dest_idx)
0339 {
0340     mac[0] = (raw_entry->mach >> 8)  & 0xff;
0341     mac[1] = (raw_entry->mach >> 0)  & 0xff;
0342     mac[2] = (raw_entry->macl >> 24) & 0xff;
0343     mac[3] = (raw_entry->macl >> 16) & 0xff;
0344     mac[4] = (raw_entry->macl >> 8)  & 0xff;
0345     mac[5] = (raw_entry->macl >> 0)  & 0xff;
0346 
0347     *vid = (raw_entry->mach >> 16) & 0xfff;
0348     *dest_idx = ANA_MACACCESS_DEST_IDX_GET(raw_entry->maca);
0349 }
0350 
0351 static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row,
0352                     struct lan966x_mac_raw_entry *raw_entries)
0353 {
0354     struct lan966x_mac_entry *mac_entry, *tmp;
0355     unsigned char mac[ETH_ALEN] __aligned(2);
0356     struct list_head mac_deleted_entries;
0357     u32 dest_idx;
0358     u32 column;
0359     u16 vid;
0360 
0361     INIT_LIST_HEAD(&mac_deleted_entries);
0362 
0363     spin_lock(&lan966x->mac_lock);
0364     list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, list) {
0365         bool found = false;
0366 
0367         if (mac_entry->row != row)
0368             continue;
0369 
0370         for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) {
0371             /* All the valid entries are at the start of the row,
0372              * so when get one invalid entry it can just skip the
0373              * rest of the columns
0374              */
0375             if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca))
0376                 break;
0377 
0378             lan966x_mac_process_raw_entry(&raw_entries[column],
0379                               mac, &vid, &dest_idx);
0380             if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
0381                 continue;
0382 
0383             /* If the entry in SW is found, then there is nothing
0384              * to do
0385              */
0386             if (mac_entry->vid == vid &&
0387                 ether_addr_equal(mac_entry->mac, mac) &&
0388                 mac_entry->port_index == dest_idx) {
0389                 raw_entries[column].processed = true;
0390                 found = true;
0391                 break;
0392             }
0393         }
0394 
0395         if (!found) {
0396             list_del(&mac_entry->list);
0397             /* Move the entry from SW list to a tmp list such that
0398              * it would be deleted later
0399              */
0400             list_add_tail(&mac_entry->list, &mac_deleted_entries);
0401         }
0402     }
0403     spin_unlock(&lan966x->mac_lock);
0404 
0405     list_for_each_entry_safe(mac_entry, tmp, &mac_deleted_entries, list) {
0406         /* Notify the bridge that the entry doesn't exist
0407          * anymore in the HW
0408          */
0409         lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
0410                       mac_entry->mac, mac_entry->vid,
0411                       lan966x->ports[mac_entry->port_index]->dev);
0412         list_del(&mac_entry->list);
0413         kfree(mac_entry);
0414     }
0415 
0416     /* Now go to the list of columns and see if any entry was not in the SW
0417      * list, then that means that the entry is new so it needs to notify the
0418      * bridge.
0419      */
0420     for (column = 0; column < LAN966X_MAC_COLUMNS; ++column) {
0421         /* All the valid entries are at the start of the row, so when
0422          * get one invalid entry it can just skip the rest of the columns
0423          */
0424         if (!ANA_MACACCESS_VALID_GET(raw_entries[column].maca))
0425             break;
0426 
0427         /* If the entry already exists then don't do anything */
0428         if (raw_entries[column].processed)
0429             continue;
0430 
0431         lan966x_mac_process_raw_entry(&raw_entries[column],
0432                           mac, &vid, &dest_idx);
0433         if (WARN_ON(dest_idx >= lan966x->num_phys_ports))
0434             continue;
0435 
0436         spin_lock(&lan966x->mac_lock);
0437         mac_entry = lan966x_mac_find_entry(lan966x, mac, vid, dest_idx);
0438         if (mac_entry) {
0439             spin_unlock(&lan966x->mac_lock);
0440             continue;
0441         }
0442 
0443         mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx);
0444         if (!mac_entry) {
0445             spin_unlock(&lan966x->mac_lock);
0446             return;
0447         }
0448 
0449         mac_entry->row = row;
0450         list_add_tail(&mac_entry->list, &lan966x->mac_entries);
0451         spin_unlock(&lan966x->mac_lock);
0452 
0453         lan966x_mac_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
0454                       mac, vid, lan966x->ports[dest_idx]->dev);
0455     }
0456 }
0457 
0458 irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x)
0459 {
0460     struct lan966x_mac_raw_entry entry[LAN966X_MAC_COLUMNS] = { 0 };
0461     u32 index, column;
0462     bool stop = true;
0463     u32 val;
0464 
0465     /* Start the scan from 0, 0 */
0466     lan_wr(ANA_MACTINDX_M_INDEX_SET(0) |
0467            ANA_MACTINDX_BUCKET_SET(0),
0468            lan966x, ANA_MACTINDX);
0469 
0470     while (1) {
0471         spin_lock(&lan966x->mac_lock);
0472         lan_rmw(ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_SYNC_GET_NEXT),
0473             ANA_MACACCESS_MAC_TABLE_CMD,
0474             lan966x, ANA_MACACCESS);
0475         lan966x_mac_wait_for_completion(lan966x);
0476 
0477         val = lan_rd(lan966x, ANA_MACTINDX);
0478         index = ANA_MACTINDX_M_INDEX_GET(val);
0479         column = ANA_MACTINDX_BUCKET_GET(val);
0480 
0481         /* The SYNC-GET-NEXT returns all the entries(4) in a row in
0482          * which is suffered a change. By change it means that new entry
0483          * was added or an entry was removed because of ageing.
0484          * It would return all the columns for that row. And after that
0485          * it would return the next row The stop conditions of the
0486          * SYNC-GET-NEXT is when it reaches 'directly' to row 0
0487          * column 3. So if SYNC-GET-NEXT returns row 0 and column 0
0488          * then it is required to continue to read more even if it
0489          * reaches row 0 and column 3.
0490          */
0491         if (index == 0 && column == 0)
0492             stop = false;
0493 
0494         if (column == LAN966X_MAC_COLUMNS - 1 &&
0495             index == 0 && stop) {
0496             spin_unlock(&lan966x->mac_lock);
0497             break;
0498         }
0499 
0500         entry[column].mach = lan_rd(lan966x, ANA_MACHDATA);
0501         entry[column].macl = lan_rd(lan966x, ANA_MACLDATA);
0502         entry[column].maca = lan_rd(lan966x, ANA_MACACCESS);
0503         spin_unlock(&lan966x->mac_lock);
0504 
0505         /* Once all the columns are read process them */
0506         if (column == LAN966X_MAC_COLUMNS - 1) {
0507             lan966x_mac_irq_process(lan966x, index, entry);
0508             /* A row was processed so it is safe to assume that the
0509              * next row/column can be the stop condition
0510              */
0511             stop = true;
0512         }
0513     }
0514 
0515     lan_rmw(ANA_ANAINTR_INTR_SET(0),
0516         ANA_ANAINTR_INTR,
0517         lan966x, ANA_ANAINTR);
0518 
0519     return IRQ_HANDLED;
0520 }