| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Roger Quadros | 4841 | 99.94% | 6 | 85.71% |
| Kees Cook | 3 | 0.06% | 1 | 14.29% |
| Total | 4844 | 7 |
// SPDX-License-Identifier: GPL-2.0 /* Texas Instruments PRUETH Switch Driver * * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com */ #include <linux/etherdevice.h> #include <linux/kernel.h> #include <linux/remoteproc.h> #include <net/switchdev.h> #include "icssm_prueth.h" #include "icssm_prueth_switch.h" #include "icssm_prueth_fdb_tbl.h" #define FDB_IDX_TBL_ENTRY(n) (&prueth->fdb_tbl->index_a->index_tbl_entry[n]) #define FDB_MAC_TBL_ENTRY(n) (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n]) #define FLAG_IS_STATIC BIT(0) #define FLAG_ACTIVE BIT(1) #define FDB_LEARN 1 #define FDB_PURGE 2 struct icssm_prueth_sw_fdb_work { netdevice_tracker ndev_tracker; struct work_struct work; struct prueth_emac *emac; u8 addr[ETH_ALEN]; int event; }; const struct prueth_queue_info sw_queue_infos[][NUM_QUEUES] = { [PRUETH_PORT_QUEUE_HOST] = { [PRUETH_QUEUE1] = { P0_Q1_BUFFER_OFFSET, P0_QUEUE_DESC_OFFSET, P0_Q1_BD_OFFSET, P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE2] = { P0_Q2_BUFFER_OFFSET, P0_QUEUE_DESC_OFFSET + 8, P0_Q2_BD_OFFSET, P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE3] = { P0_Q3_BUFFER_OFFSET, P0_QUEUE_DESC_OFFSET + 16, P0_Q3_BD_OFFSET, P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE4] = { P0_Q4_BUFFER_OFFSET, P0_QUEUE_DESC_OFFSET + 24, P0_Q4_BD_OFFSET, P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), }, }, [PRUETH_PORT_QUEUE_MII0] = { [PRUETH_QUEUE1] = { P1_Q1_BUFFER_OFFSET, P1_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), P1_Q1_BD_OFFSET, P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE2] = { P1_Q2_BUFFER_OFFSET, P1_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), P1_Q2_BD_OFFSET, P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE3] = { P1_Q3_BUFFER_OFFSET, P1_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), P1_Q3_BD_OFFSET, P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE4] = { P1_Q4_BUFFER_OFFSET, P1_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), P1_Q4_BD_OFFSET, P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), }, }, [PRUETH_PORT_QUEUE_MII1] = { [PRUETH_QUEUE1] = { P2_Q1_BUFFER_OFFSET, P2_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), P2_Q1_BD_OFFSET, P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE2] = { P2_Q2_BUFFER_OFFSET, P2_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), P2_Q2_BD_OFFSET, P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE3] = { P2_Q3_BUFFER_OFFSET, P2_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), P2_Q3_BD_OFFSET, P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE4] = { P2_Q4_BUFFER_OFFSET, P2_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), P2_Q4_BD_OFFSET, P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), }, }, }; static const struct prueth_queue_info rx_queue_infos[][NUM_QUEUES] = { [PRUETH_PORT_QUEUE_HOST] = { [PRUETH_QUEUE1] = { P0_Q1_BUFFER_OFFSET, HOST_QUEUE_DESC_OFFSET, P0_Q1_BD_OFFSET, P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE2] = { P0_Q2_BUFFER_OFFSET, HOST_QUEUE_DESC_OFFSET + 8, P0_Q2_BD_OFFSET, P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE3] = { P0_Q3_BUFFER_OFFSET, HOST_QUEUE_DESC_OFFSET + 16, P0_Q3_BD_OFFSET, P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE4] = { P0_Q4_BUFFER_OFFSET, HOST_QUEUE_DESC_OFFSET + 24, P0_Q4_BD_OFFSET, P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), }, }, [PRUETH_PORT_QUEUE_MII0] = { [PRUETH_QUEUE1] = { P1_Q1_BUFFER_OFFSET, P1_QUEUE_DESC_OFFSET, P1_Q1_BD_OFFSET, P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE2] = { P1_Q2_BUFFER_OFFSET, P1_QUEUE_DESC_OFFSET + 8, P1_Q2_BD_OFFSET, P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE3] = { P1_Q3_BUFFER_OFFSET, P1_QUEUE_DESC_OFFSET + 16, P1_Q3_BD_OFFSET, P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE4] = { P1_Q4_BUFFER_OFFSET, P1_QUEUE_DESC_OFFSET + 24, P1_Q4_BD_OFFSET, P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), }, }, [PRUETH_PORT_QUEUE_MII1] = { [PRUETH_QUEUE1] = { P2_Q1_BUFFER_OFFSET, P2_QUEUE_DESC_OFFSET, P2_Q1_BD_OFFSET, P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE2] = { P2_Q2_BUFFER_OFFSET, P2_QUEUE_DESC_OFFSET + 8, P2_Q2_BD_OFFSET, P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE3] = { P2_Q3_BUFFER_OFFSET, P2_QUEUE_DESC_OFFSET + 16, P2_Q3_BD_OFFSET, P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), }, [PRUETH_QUEUE4] = { P2_Q4_BUFFER_OFFSET, P2_QUEUE_DESC_OFFSET + 24, P2_Q4_BD_OFFSET, P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), }, }, }; void icssm_prueth_sw_free_fdb_table(struct prueth *prueth) { if (prueth->emac_configured) return; kfree(prueth->fdb_tbl); prueth->fdb_tbl = NULL; } void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth) { struct fdb_tbl *t = prueth->fdb_tbl; void __iomem *sram_base; u8 val; sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va; t->index_a = sram_base + V2_1_FDB_TBL_OFFSET; t->mac_tbl_a = sram_base + FDB_MAC_TBL_OFFSET; t->port1_stp_cfg = sram_base + FDB_PORT1_STP_CFG_OFFSET; t->port2_stp_cfg = sram_base + FDB_PORT2_STP_CFG_OFFSET; t->flood_enable_flags = sram_base + FDB_FLOOD_ENABLE_FLAGS_OFFSET; t->locks = sram_base + FDB_LOCKS_OFFSET; val = readb(t->flood_enable_flags); /* host_flood_enable = 1 */ val |= BIT(0); /* port1_flood_enable = 1 */ val |= BIT(1); /* port2_flood_enable = 1 */ val |= BIT(2); writeb(val, t->flood_enable_flags); writeb(0, &t->locks->host_lock); t->total_entries = 0; } static u8 icssm_pru_lock_done(struct fdb_tbl *fdb_tbl) { return readb(&fdb_tbl->locks->pru_locks); } static int icssm_prueth_sw_fdb_spin_lock(struct fdb_tbl *fdb_tbl) { u8 done; int ret; /* Take the host lock */ writeb(1, &fdb_tbl->locks->host_lock); /* Wait for the PRUs to release their locks */ ret = read_poll_timeout(icssm_pru_lock_done, done, done == 0, 1, 10, false, fdb_tbl); if (ret == -ETIMEDOUT) writeb(0, &fdb_tbl->locks->host_lock); return ret; } static void icssm_prueth_sw_fdb_spin_unlock(struct fdb_tbl *fdb_tbl) { writeb(0, &fdb_tbl->locks->host_lock); } static u8 icssm_prueth_sw_fdb_hash(const u8 *mac) { return (mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]); } static int icssm_prueth_sw_fdb_search(struct fdb_mac_tbl_array __iomem *mac_tbl, struct fdb_index_tbl_entry __iomem *bucket_info, const u8 *mac) { unsigned int bucket_entries, mac_tbl_idx; u8 tmp_mac[ETH_ALEN]; int i; mac_tbl_idx = readw(&bucket_info->bucket_idx); bucket_entries = readw(&bucket_info->bucket_entries); for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) { memcpy_fromio(tmp_mac, mac_tbl->mac_tbl_entry[mac_tbl_idx].mac, ETH_ALEN); if (ether_addr_equal(mac, tmp_mac)) return mac_tbl_idx; } return -ENODATA; } static int icssm_prueth_sw_fdb_find_open_slot(struct fdb_tbl *fdb_tbl) { unsigned int i; u8 flags; for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { flags = readb(&fdb_tbl->mac_tbl_a->mac_tbl_entry[i].flags); if (!(flags & FLAG_ACTIVE)) break; } return i; } static int icssm_prueth_sw_find_fdb_insert(struct fdb_tbl *fdb, struct prueth *prueth, struct fdb_index_tbl_entry __iomem *bkt_info, const u8 *mac, const u8 port) { struct fdb_mac_tbl_array __iomem *mac_tbl = fdb->mac_tbl_a; unsigned int bucket_entries, mac_tbl_idx; struct fdb_mac_tbl_entry __iomem *e; u8 mac_from_hw[ETH_ALEN]; s8 cmp; int i; mac_tbl_idx = readw(&bkt_info->bucket_idx); bucket_entries = readw(&bkt_info->bucket_entries); for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) { e = &mac_tbl->mac_tbl_entry[mac_tbl_idx]; memcpy_fromio(mac_from_hw, e->mac, ETH_ALEN); cmp = memcmp(mac, mac_from_hw, ETH_ALEN); if (cmp < 0) { return mac_tbl_idx; } else if (cmp == 0) { if (readb(&e->port) != port) { /* MAC is already in FDB, only port is * different. So just update the port. * Note: total_entries and bucket_entries * remain the same. */ writeb(port, &e->port); } /* MAC and port are the same, touch the fdb */ writew(0, &e->age); return -EEXIST; } } return mac_tbl_idx; } static int icssm_prueth_sw_fdb_empty_slot_left(struct fdb_mac_tbl_array __iomem *mac_tbl, unsigned int mac_tbl_idx) { u8 flags; int i; for (i = mac_tbl_idx - 1; i > -1; i--) { flags = readb(&mac_tbl->mac_tbl_entry[i].flags); if (!(flags & FLAG_ACTIVE)) break; } return i; } static int icssm_prueth_sw_fdb_empty_slot_right(struct fdb_mac_tbl_array __iomem *mac_tbl, unsigned int mac_tbl_idx) { u8 flags; int i; for (i = mac_tbl_idx; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { flags = readb(&mac_tbl->mac_tbl_entry[i].flags); if (!(flags & FLAG_ACTIVE)) return i; } return -1; } static void icssm_prueth_sw_fdb_move_range_left(struct prueth *prueth, u16 left, u16 right) { struct fdb_mac_tbl_entry entry; u32 sz = 0; u16 i; sz = sizeof(struct fdb_mac_tbl_entry); for (i = left; i < right; i++) { memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i + 1), sz); memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz); } } static void icssm_prueth_sw_fdb_move_range_right(struct prueth *prueth, u16 left, u16 right) { struct fdb_mac_tbl_entry entry; u32 sz = 0; u16 i; sz = sizeof(struct fdb_mac_tbl_entry); for (i = right; i > left; i--) { memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i - 1), sz); memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz); } } static void icssm_prueth_sw_fdb_update_index_tbl(struct prueth *prueth, u16 left, u16 right) { unsigned int hash, hash_prev; u8 mac[ETH_ALEN]; unsigned int i; /* To ensure we don't improperly update the * bucket index, initialize with an invalid * hash in case we are in leftmost slot */ hash_prev = 0xff; if (left > 0) { memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(left - 1)->mac, ETH_ALEN); hash_prev = icssm_prueth_sw_fdb_hash(mac); } /* For each moved element, update the bucket index */ for (i = left; i <= right; i++) { memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac, ETH_ALEN); hash = icssm_prueth_sw_fdb_hash(mac); /* Only need to update buckets once */ if (hash != hash_prev) writew(i, &FDB_IDX_TBL_ENTRY(hash)->bucket_idx); hash_prev = hash; } } static struct fdb_mac_tbl_entry __iomem * icssm_prueth_sw_find_free_mac(struct prueth *prueth, struct fdb_index_tbl_entry __iomem *bucket_info, u8 suggested_mac_tbl_idx, bool *update_indexes, const u8 *mac) { s16 empty_slot_idx = 0, left = 0, right = 0; unsigned int mti = suggested_mac_tbl_idx; struct fdb_mac_tbl_array __iomem *mt; struct fdb_tbl *fdb; u8 flags; fdb = prueth->fdb_tbl; mt = fdb->mac_tbl_a; flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags); if (!(flags & FLAG_ACTIVE)) { /* Claim the entry */ flags |= FLAG_ACTIVE; writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags); return FDB_MAC_TBL_ENTRY(mti); } if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) return NULL; empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_left(mt, mti); if (empty_slot_idx == -1) { /* Nothing available on the left. But table isn't full * so there must be space to the right, */ empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_right(mt, mti); /* Shift right */ left = mti; right = empty_slot_idx; icssm_prueth_sw_fdb_move_range_right(prueth, left, right); /* Claim the entry */ flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags); flags |= FLAG_ACTIVE; writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags); memcpy_toio(FDB_MAC_TBL_ENTRY(mti)->mac, mac, ETH_ALEN); /* There is a chance we moved something in a * different bucket, update index table */ icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right); return FDB_MAC_TBL_ENTRY(mti); } if (empty_slot_idx == mti - 1) { /* There is space immediately left of the open slot, * which means the inserted MAC address * must be the lowest-valued MAC address in bucket. * Update bucket pointer accordingly. */ writew(empty_slot_idx, &bucket_info->bucket_idx); /* Claim the entry */ flags = readb(&FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags); flags |= FLAG_ACTIVE; writeb(flags, &FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags); return FDB_MAC_TBL_ENTRY(empty_slot_idx); } /* There is empty space to the left, shift MAC table entries left */ left = empty_slot_idx; right = mti - 1; icssm_prueth_sw_fdb_move_range_left(prueth, left, right); /* Claim the entry */ flags = readb(&FDB_MAC_TBL_ENTRY(mti - 1)->flags); flags |= FLAG_ACTIVE; writeb(flags, &FDB_MAC_TBL_ENTRY(mti - 1)->flags); memcpy_toio(FDB_MAC_TBL_ENTRY(mti - 1)->mac, mac, ETH_ALEN); /* There is a chance we moved something in a * different bucket, update index table */ icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right); return FDB_MAC_TBL_ENTRY(mti - 1); } static int icssm_prueth_sw_insert_fdb_entry(struct prueth_emac *emac, const u8 *mac, u8 is_static) { struct fdb_index_tbl_entry __iomem *bucket_info; struct fdb_mac_tbl_entry __iomem *mac_info; struct prueth *prueth = emac->prueth; unsigned int hash_val, mac_tbl_idx; struct prueth_emac *other_emac; enum prueth_port other_port_id; int total_fdb_entries; struct fdb_tbl *fdb; u8 flags; s16 ret; int err; u16 val; fdb = prueth->fdb_tbl; other_port_id = (emac->port_id == PRUETH_PORT_MII0) ? PRUETH_PORT_MII1 : PRUETH_PORT_MII0; other_emac = prueth->emac[other_port_id - 1]; if (!other_emac) return -EINVAL; err = icssm_prueth_sw_fdb_spin_lock(fdb); if (err) { dev_err(prueth->dev, "PRU lock timeout %d\n", err); return err; } if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) { icssm_prueth_sw_fdb_spin_unlock(fdb); return -ENOMEM; } if (ether_addr_equal(mac, emac->mac_addr) || (ether_addr_equal(mac, other_emac->mac_addr))) { icssm_prueth_sw_fdb_spin_unlock(fdb); /* Don't insert fdb of own mac addr */ return -EINVAL; } /* Get the bucket that the mac belongs to */ hash_val = icssm_prueth_sw_fdb_hash(mac); bucket_info = FDB_IDX_TBL_ENTRY(hash_val); if (!readw(&bucket_info->bucket_entries)) { mac_tbl_idx = icssm_prueth_sw_fdb_find_open_slot(fdb); writew(mac_tbl_idx, &bucket_info->bucket_idx); } ret = icssm_prueth_sw_find_fdb_insert(fdb, prueth, bucket_info, mac, emac->port_id - 1); if (ret < 0) { icssm_prueth_sw_fdb_spin_unlock(fdb); /* mac is already in fdb table */ return 0; } mac_tbl_idx = ret; mac_info = icssm_prueth_sw_find_free_mac(prueth, bucket_info, mac_tbl_idx, NULL, mac); if (!mac_info) { /* Should not happen */ dev_warn(prueth->dev, "OUT of FDB MEM\n"); icssm_prueth_sw_fdb_spin_unlock(fdb); return -ENOMEM; } memcpy_toio(mac_info->mac, mac, ETH_ALEN); writew(0, &mac_info->age); writeb(emac->port_id - 1, &mac_info->port); flags = readb(&mac_info->flags); if (is_static) flags |= FLAG_IS_STATIC; else flags &= ~FLAG_IS_STATIC; /* bit 1 - active */ flags |= FLAG_ACTIVE; writeb(flags, &mac_info->flags); val = readw(&bucket_info->bucket_entries); val++; writew(val, &bucket_info->bucket_entries); fdb->total_entries++; total_fdb_entries = fdb->total_entries; icssm_prueth_sw_fdb_spin_unlock(fdb); dev_dbg(prueth->dev, "added fdb: %pM port=%d total_entries=%u\n", mac, emac->port_id, total_fdb_entries); return 0; } static int icssm_prueth_sw_delete_fdb_entry(struct prueth_emac *emac, const u8 *mac, u8 is_static) { struct fdb_index_tbl_entry __iomem *bucket_info; struct fdb_mac_tbl_entry __iomem *mac_info; struct fdb_mac_tbl_array __iomem *mt; unsigned int hash_val, mac_tbl_idx; unsigned int idx, entries; struct prueth *prueth; int total_fdb_entries; s16 ret, left, right; struct fdb_tbl *fdb; u8 flags; int err; u16 val; prueth = emac->prueth; fdb = prueth->fdb_tbl; mt = fdb->mac_tbl_a; err = icssm_prueth_sw_fdb_spin_lock(fdb); if (err) { dev_err(prueth->dev, "PRU lock timeout %d\n", err); return err; } if (fdb->total_entries == 0) { icssm_prueth_sw_fdb_spin_unlock(fdb); return 0; } /* Get the bucket that the mac belongs to */ hash_val = icssm_prueth_sw_fdb_hash(mac); bucket_info = FDB_IDX_TBL_ENTRY(hash_val); ret = icssm_prueth_sw_fdb_search(mt, bucket_info, mac); if (ret < 0) { icssm_prueth_sw_fdb_spin_unlock(fdb); return ret; } mac_tbl_idx = ret; mac_info = FDB_MAC_TBL_ENTRY(mac_tbl_idx); /* Shift all elements in bucket to the left. No need to * update index table since only shifting within bucket. */ left = mac_tbl_idx; idx = readw(&bucket_info->bucket_idx); entries = readw(&bucket_info->bucket_entries); right = idx + entries - 1; icssm_prueth_sw_fdb_move_range_left(prueth, left, right); /* Remove end of bucket from table */ mac_info = FDB_MAC_TBL_ENTRY(right); flags = readb(&mac_info->flags); /* active = 0 */ flags &= ~FLAG_ACTIVE; writeb(flags, &mac_info->flags); val = readw(&bucket_info->bucket_entries); val--; writew(val, &bucket_info->bucket_entries); fdb->total_entries--; total_fdb_entries = fdb->total_entries; icssm_prueth_sw_fdb_spin_unlock(fdb); dev_dbg(prueth->dev, "del fdb: %pM total_entries=%u\n", mac, total_fdb_entries); return 0; } int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac) { struct fdb_index_tbl_entry __iomem *bucket_info; struct prueth *prueth = emac->prueth; u8 flags, mac[ETH_ALEN]; unsigned int hash_val; struct fdb_tbl *fdb; int ret, i; u16 val; fdb = prueth->fdb_tbl; ret = icssm_prueth_sw_fdb_spin_lock(fdb); if (ret) { dev_err(prueth->dev, "PRU lock timeout %d\n", ret); return ret; } if (fdb->total_entries == 0) { icssm_prueth_sw_fdb_spin_unlock(fdb); return 0; } for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { flags = readb(&fdb->mac_tbl_a->mac_tbl_entry[i].flags); if ((flags & FLAG_ACTIVE) && !(flags & FLAG_IS_STATIC)) { /* Get the bucket that the mac belongs to */ memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac, ETH_ALEN); hash_val = icssm_prueth_sw_fdb_hash(mac); bucket_info = FDB_IDX_TBL_ENTRY(hash_val); flags &= ~FLAG_ACTIVE; writeb(flags, &fdb->mac_tbl_a->mac_tbl_entry[i].flags); val = readw(&bucket_info->bucket_entries); val--; writew(val, &bucket_info->bucket_entries); fdb->total_entries--; } } icssm_prueth_sw_fdb_spin_unlock(fdb); return 0; } int icssm_prueth_sw_init_fdb_table(struct prueth *prueth) { if (prueth->emac_configured) return 0; prueth->fdb_tbl = kmalloc_obj(*prueth->fdb_tbl); if (!prueth->fdb_tbl) return -ENOMEM; icssm_prueth_sw_fdb_tbl_init(prueth); return 0; } /** * icssm_prueth_sw_fdb_add - insert fdb entry * * @emac: EMAC data structure * @fdb: fdb info * */ void icssm_prueth_sw_fdb_add(struct prueth_emac *emac, struct switchdev_notifier_fdb_info *fdb) { icssm_prueth_sw_insert_fdb_entry(emac, fdb->addr, 1); } /** * icssm_prueth_sw_fdb_del - delete fdb entry * * @emac: EMAC data structure * @fdb: fdb info * */ void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, struct switchdev_notifier_fdb_info *fdb) { icssm_prueth_sw_delete_fdb_entry(emac, fdb->addr, 1); } static void icssm_prueth_sw_fdb_work(struct work_struct *work) { struct icssm_prueth_sw_fdb_work *fdb_work = container_of(work, struct icssm_prueth_sw_fdb_work, work); struct prueth_emac *emac = fdb_work->emac; rtnl_lock(); /* Interface is not up */ if (!emac->prueth->fdb_tbl) goto free; switch (fdb_work->event) { case FDB_LEARN: icssm_prueth_sw_insert_fdb_entry(emac, fdb_work->addr, 0); break; case FDB_PURGE: icssm_prueth_sw_do_purge_fdb(emac); break; default: break; } free: rtnl_unlock(); netdev_put(emac->ndev, &fdb_work->ndev_tracker); kfree(fdb_work); } int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac) { struct icssm_prueth_sw_fdb_work *fdb_work; fdb_work = kzalloc_obj(*fdb_work, GFP_ATOMIC); if (WARN_ON(!fdb_work)) return -ENOMEM; INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work); fdb_work->event = FDB_LEARN; fdb_work->emac = emac; ether_addr_copy(fdb_work->addr, src_mac); netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC); queue_work(system_long_wq, &fdb_work->work); return 0; } int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac) { struct icssm_prueth_sw_fdb_work *fdb_work; fdb_work = kzalloc_obj(*fdb_work, GFP_ATOMIC); if (WARN_ON(!fdb_work)) return -ENOMEM; INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work); fdb_work->event = FDB_PURGE; fdb_work->emac = emac; netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC); queue_work(system_long_wq, &fdb_work->work); return 0; } void icssm_prueth_sw_hostconfig(struct prueth *prueth) { void __iomem *dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va; void __iomem *dram; /* queue information table */ dram = dram1_base + P0_Q1_RX_CONTEXT_OFFSET; memcpy_toio(dram, sw_queue_infos[PRUETH_PORT_QUEUE_HOST], sizeof(sw_queue_infos[PRUETH_PORT_QUEUE_HOST])); /* buffer descriptor offset table*/ dram = dram1_base + QUEUE_DESCRIPTOR_OFFSET_ADDR; writew(P0_Q1_BD_OFFSET, dram); writew(P0_Q2_BD_OFFSET, dram + 2); writew(P0_Q3_BD_OFFSET, dram + 4); writew(P0_Q4_BD_OFFSET, dram + 6); /* buffer offset table */ dram = dram1_base + QUEUE_OFFSET_ADDR; writew(P0_Q1_BUFFER_OFFSET, dram); writew(P0_Q2_BUFFER_OFFSET, dram + 2); writew(P0_Q3_BUFFER_OFFSET, dram + 4); writew(P0_Q4_BUFFER_OFFSET, dram + 6); /* queue size lookup table */ dram = dram1_base + QUEUE_SIZE_ADDR; writew(HOST_QUEUE_1_SIZE, dram); writew(HOST_QUEUE_1_SIZE, dram + 2); writew(HOST_QUEUE_1_SIZE, dram + 4); writew(HOST_QUEUE_1_SIZE, dram + 6); /* queue table */ dram = dram1_base + P0_QUEUE_DESC_OFFSET; memcpy_toio(dram, queue_descs[PRUETH_PORT_QUEUE_HOST], sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); } static int icssm_prueth_sw_port_config(struct prueth *prueth, enum prueth_port port_id) { unsigned int tx_context_ofs_addr, rx_context_ofs, queue_desc_ofs; void __iomem *dram, *dram_base, *dram_mac; struct prueth_emac *emac; void __iomem *dram1_base; dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va; emac = prueth->emac[port_id - 1]; switch (port_id) { case PRUETH_PORT_MII0: tx_context_ofs_addr = TX_CONTEXT_P1_Q1_OFFSET_ADDR; rx_context_ofs = P1_Q1_RX_CONTEXT_OFFSET; queue_desc_ofs = P1_QUEUE_DESC_OFFSET; /* for switch PORT MII0 mac addr is in DRAM0. */ dram_mac = prueth->mem[PRUETH_MEM_DRAM0].va; break; case PRUETH_PORT_MII1: tx_context_ofs_addr = TX_CONTEXT_P2_Q1_OFFSET_ADDR; rx_context_ofs = P2_Q1_RX_CONTEXT_OFFSET; queue_desc_ofs = P2_QUEUE_DESC_OFFSET; /* for switch PORT MII1 mac addr is in DRAM1. */ dram_mac = prueth->mem[PRUETH_MEM_DRAM1].va; break; default: netdev_err(emac->ndev, "invalid port\n"); return -EINVAL; } /* setup mac address */ memcpy_toio(dram_mac + PORT_MAC_ADDR, emac->mac_addr, 6); /* Remaining switch port configs are in DRAM1 */ dram_base = prueth->mem[PRUETH_MEM_DRAM1].va; /* queue information table */ memcpy_toio(dram_base + tx_context_ofs_addr, sw_queue_infos[port_id], sizeof(sw_queue_infos[port_id])); memcpy_toio(dram_base + rx_context_ofs, rx_queue_infos[port_id], sizeof(rx_queue_infos[port_id])); /* buffer descriptor offset table*/ dram = dram_base + QUEUE_DESCRIPTOR_OFFSET_ADDR + (port_id * NUM_QUEUES * sizeof(u16)); writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_desc_offset, dram); writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_desc_offset, dram + 2); writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_desc_offset, dram + 4); writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_desc_offset, dram + 6); /* buffer offset table */ dram = dram_base + QUEUE_OFFSET_ADDR + port_id * NUM_QUEUES * sizeof(u16); writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_offset, dram); writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_offset, dram + 2); writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_offset, dram + 4); writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_offset, dram + 6); /* queue size lookup table */ dram = dram_base + QUEUE_SIZE_ADDR + port_id * NUM_QUEUES * sizeof(u16); writew(QUEUE_1_SIZE, dram); writew(QUEUE_2_SIZE, dram + 2); writew(QUEUE_3_SIZE, dram + 4); writew(QUEUE_4_SIZE, dram + 6); /* queue table */ memcpy_toio(dram_base + queue_desc_ofs, &queue_descs[port_id][0], 4 * sizeof(queue_descs[port_id][0])); emac->rx_queue_descs = dram1_base + P0_QUEUE_DESC_OFFSET; emac->tx_queue_descs = dram1_base + rx_queue_infos[port_id][PRUETH_QUEUE1].queue_desc_offset; return 0; } int icssm_prueth_sw_emac_config(struct prueth_emac *emac) { struct prueth *prueth = emac->prueth; u32 sharedramaddr, ocmcaddr; int ret; /* PRU needs local shared RAM address for C28 */ sharedramaddr = ICSS_LOCAL_SHARED_RAM; /* PRU needs real global OCMC address for C30*/ ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; if (prueth->emac_configured & BIT(emac->port_id)) return 0; ret = icssm_prueth_sw_port_config(prueth, emac->port_id); if (ret) return ret; if (!prueth->emac_configured) { /* Set in constant table C28 of PRUn to ICSS Shared memory */ pru_rproc_set_ctable(prueth->pru0, PRU_C28, sharedramaddr); pru_rproc_set_ctable(prueth->pru1, PRU_C28, sharedramaddr); /* Set in constant table C30 of PRUn to OCMC memory */ pru_rproc_set_ctable(prueth->pru0, PRU_C30, ocmcaddr); pru_rproc_set_ctable(prueth->pru1, PRU_C30, ocmcaddr); } return 0; } int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev) { const struct prueth_firmware *pru_firmwares; const char *fw_name, *fw_name1; int ret; if (prueth->emac_configured) return 0; pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU0]; fw_name = pru_firmwares->fw_name[prueth->eth_type]; pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU1]; fw_name1 = pru_firmwares->fw_name[prueth->eth_type]; ret = rproc_set_firmware(prueth->pru0, fw_name); if (ret) { netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n", fw_name, ret); return ret; } ret = rproc_boot(prueth->pru0); if (ret) { netdev_err(ndev, "failed to boot PRU0: %d\n", ret); return ret; } ret = rproc_set_firmware(prueth->pru1, fw_name1); if (ret) { netdev_err(ndev, "failed to set PRU1 firmware %s: %d\n", fw_name1, ret); goto rproc0_shutdown; } ret = rproc_boot(prueth->pru1); if (ret) { netdev_err(ndev, "failed to boot PRU1: %d\n", ret); goto rproc0_shutdown; } return 0; rproc0_shutdown: rproc_shutdown(prueth->pru0); return ret; } int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac, struct net_device *ndev) { struct prueth *prueth = emac->prueth; if (prueth->emac_configured) return 0; rproc_shutdown(prueth->pru0); rproc_shutdown(prueth->pru1); return 0; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1