Skip to content

Commit 0d3b33e

Browse files
viviendavem330
authored andcommitted
net: dsa: mv88e6xxx: add VLAN Load support
Implement port_pvid_set and port_vlan_add to add new entries in the VLAN hardware table, and join ports to them. The patch also implement the STU Get Next and Load Purge operations, since it is required to have a valid STU entry for at least all VLANs. Each VLAN has its own forwarding database, with FID num_ports+1 to 4095. Signed-off-by: Vivien Didelot <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7dad08d commit 0d3b33e

File tree

3 files changed

+180
-0
lines changed

3 files changed

+180
-0
lines changed

drivers/net/dsa/mv88e6352.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
344344
.port_leave_bridge = mv88e6xxx_leave_bridge,
345345
.port_stp_update = mv88e6xxx_port_stp_update,
346346
.port_pvid_get = mv88e6xxx_port_pvid_get,
347+
.port_pvid_set = mv88e6xxx_port_pvid_set,
348+
.port_vlan_add = mv88e6xxx_port_vlan_add,
347349
.port_vlan_del = mv88e6xxx_port_vlan_del,
348350
.vlan_getnext = mv88e6xxx_vlan_getnext,
349351
.port_fdb_add = mv88e6xxx_port_fdb_add,

drivers/net/dsa/mv88e6xxx.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,12 @@ int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
11981198
return 0;
11991199
}
12001200

1201+
int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
1202+
{
1203+
return mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
1204+
pvid & PORT_DEFAULT_VLAN_MASK);
1205+
}
1206+
12011207
static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
12021208
{
12031209
return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
@@ -1374,6 +1380,169 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
13741380
return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
13751381
}
13761382

1383+
static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
1384+
struct mv88e6xxx_vtu_stu_entry *entry)
1385+
{
1386+
struct mv88e6xxx_vtu_stu_entry next = { 0 };
1387+
int ret;
1388+
1389+
ret = _mv88e6xxx_vtu_wait(ds);
1390+
if (ret < 0)
1391+
return ret;
1392+
1393+
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID,
1394+
sid & GLOBAL_VTU_SID_MASK);
1395+
if (ret < 0)
1396+
return ret;
1397+
1398+
ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT);
1399+
if (ret < 0)
1400+
return ret;
1401+
1402+
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID);
1403+
if (ret < 0)
1404+
return ret;
1405+
1406+
next.sid = ret & GLOBAL_VTU_SID_MASK;
1407+
1408+
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1409+
if (ret < 0)
1410+
return ret;
1411+
1412+
next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1413+
1414+
if (next.valid) {
1415+
ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2);
1416+
if (ret < 0)
1417+
return ret;
1418+
}
1419+
1420+
*entry = next;
1421+
return 0;
1422+
}
1423+
1424+
static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
1425+
struct mv88e6xxx_vtu_stu_entry *entry)
1426+
{
1427+
u16 reg = 0;
1428+
int ret;
1429+
1430+
ret = _mv88e6xxx_vtu_wait(ds);
1431+
if (ret < 0)
1432+
return ret;
1433+
1434+
if (!entry->valid)
1435+
goto loadpurge;
1436+
1437+
/* Write port states */
1438+
ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2);
1439+
if (ret < 0)
1440+
return ret;
1441+
1442+
reg = GLOBAL_VTU_VID_VALID;
1443+
loadpurge:
1444+
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1445+
if (ret < 0)
1446+
return ret;
1447+
1448+
reg = entry->sid & GLOBAL_VTU_SID_MASK;
1449+
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1450+
if (ret < 0)
1451+
return ret;
1452+
1453+
return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE);
1454+
}
1455+
1456+
static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
1457+
struct mv88e6xxx_vtu_stu_entry *entry)
1458+
{
1459+
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1460+
struct mv88e6xxx_vtu_stu_entry vlan = {
1461+
.valid = true,
1462+
.vid = vid,
1463+
};
1464+
int i;
1465+
1466+
/* exclude all ports except the CPU */
1467+
for (i = 0; i < ps->num_ports; ++i)
1468+
vlan.data[i] = dsa_is_cpu_port(ds, i) ?
1469+
GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED :
1470+
GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1471+
1472+
if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
1473+
mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
1474+
struct mv88e6xxx_vtu_stu_entry vstp;
1475+
int err;
1476+
1477+
/* Adding a VTU entry requires a valid STU entry. As VSTP is not
1478+
* implemented, only one STU entry is needed to cover all VTU
1479+
* entries. Thus, validate the SID 0.
1480+
*/
1481+
vlan.sid = 0;
1482+
err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp);
1483+
if (err)
1484+
return err;
1485+
1486+
if (vstp.sid != vlan.sid || !vstp.valid) {
1487+
memset(&vstp, 0, sizeof(vstp));
1488+
vstp.valid = true;
1489+
vstp.sid = vlan.sid;
1490+
1491+
err = _mv88e6xxx_stu_loadpurge(ds, &vstp);
1492+
if (err)
1493+
return err;
1494+
}
1495+
1496+
/* Non-bridged ports and bridge groups use FIDs from 1 to
1497+
* num_ports; VLANs use FIDs from num_ports+1 to 4095.
1498+
*/
1499+
vlan.fid = find_next_zero_bit(ps->fid_bitmap, VLAN_N_VID,
1500+
ps->num_ports + 1);
1501+
if (unlikely(vlan.fid == VLAN_N_VID)) {
1502+
pr_err("no more FID available for VLAN %d\n", vid);
1503+
return -ENOSPC;
1504+
}
1505+
1506+
err = _mv88e6xxx_flush_fid(ds, vlan.fid);
1507+
if (err)
1508+
return err;
1509+
1510+
set_bit(vlan.fid, ps->fid_bitmap);
1511+
}
1512+
1513+
*entry = vlan;
1514+
return 0;
1515+
}
1516+
1517+
int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
1518+
bool untagged)
1519+
{
1520+
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1521+
struct mv88e6xxx_vtu_stu_entry vlan;
1522+
int err;
1523+
1524+
mutex_lock(&ps->smi_mutex);
1525+
err = _mv88e6xxx_vtu_getnext(ds, vid - 1, &vlan);
1526+
if (err)
1527+
goto unlock;
1528+
1529+
if (vlan.vid != vid || !vlan.valid) {
1530+
err = _mv88e6xxx_vlan_init(ds, vid, &vlan);
1531+
if (err)
1532+
goto unlock;
1533+
}
1534+
1535+
vlan.data[port] = untagged ?
1536+
GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1537+
GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1538+
1539+
err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1540+
unlock:
1541+
mutex_unlock(&ps->smi_mutex);
1542+
1543+
return err;
1544+
}
1545+
13771546
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
13781547
{
13791548
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);

drivers/net/dsa/mv88e6xxx.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@
197197
#define GLOBAL_VTU_OP_FLUSH_ALL ((0x01 << 12) | GLOBAL_VTU_OP_BUSY)
198198
#define GLOBAL_VTU_OP_VTU_LOAD_PURGE ((0x03 << 12) | GLOBAL_VTU_OP_BUSY)
199199
#define GLOBAL_VTU_OP_VTU_GET_NEXT ((0x04 << 12) | GLOBAL_VTU_OP_BUSY)
200+
#define GLOBAL_VTU_OP_STU_LOAD_PURGE ((0x05 << 12) | GLOBAL_VTU_OP_BUSY)
201+
#define GLOBAL_VTU_OP_STU_GET_NEXT ((0x06 << 12) | GLOBAL_VTU_OP_BUSY)
200202
#define GLOBAL_VTU_VID 0x06
201203
#define GLOBAL_VTU_VID_MASK 0xfff
202204
#define GLOBAL_VTU_VID_VALID BIT(12)
@@ -208,6 +210,10 @@
208210
#define GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED 0x01
209211
#define GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED 0x02
210212
#define GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER 0x03
213+
#define GLOBAL_STU_DATA_PORT_STATE_DISABLED 0x00
214+
#define GLOBAL_STU_DATA_PORT_STATE_BLOCKING 0x01
215+
#define GLOBAL_STU_DATA_PORT_STATE_LEARNING 0x02
216+
#define GLOBAL_STU_DATA_PORT_STATE_FORWARDING 0x03
211217
#define GLOBAL_ATU_CONTROL 0x0a
212218
#define GLOBAL_ATU_CONTROL_LEARN2ALL BIT(3)
213219
#define GLOBAL_ATU_OP 0x0b
@@ -454,6 +460,9 @@ int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
454460
int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
455461
int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
456462
int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
463+
int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid);
464+
int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
465+
bool untagged);
457466
int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid);
458467
int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
459468
unsigned long *ports, unsigned long *untagged);

0 commit comments

Comments
 (0)