Skip to content

Commit

Permalink
Merge branch 'netcp-next'
Browse files Browse the repository at this point in the history
Murali Karicheri says:

====================
netcp: enhancements and minor fixes

This series is for net-next. This propagates enhancements and minor
bug fixes from internal version of the driver to keep the upstream
in sync. Please review and apply if this looks good.

Tested on all of K2HK/E/L boards with nfs rootfs.
Test logs below
K2HK-EVM: http://pastebin.ubuntu.com/23754106/
k2L-EVM: http://pastebin.ubuntu.com/23754143/
K2E-EVM: http://pastebin.ubuntu.com/23754159/

History:
  v1 - dropped 1/10 amd 2/10 of v0 based on comments from Rob as
       it needs more work before submission
  v0 - Initial version
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jan 8, 2017
2 parents b14ad90 + b361da8 commit 82e4869
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 53 deletions.
180 changes: 155 additions & 25 deletions drivers/net/ethernet/ti/cpsw_ale.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Texas Instruments 3-Port Ethernet Switch Address Lookup Engine
* Texas Instruments N-Port Ethernet Switch Address Lookup Engine
*
* Copyright (C) 2012 Texas Instruments
*
Expand Down Expand Up @@ -27,18 +27,28 @@

#define BITMASK(bits) (BIT(bits) - 1)

#define ALE_VERSION_MAJOR(rev) ((rev >> 8) & 0xff)
#define ALE_VERSION_MAJOR(rev, mask) (((rev) >> 8) & (mask))
#define ALE_VERSION_MINOR(rev) (rev & 0xff)
#define ALE_VERSION_1R3 0x0103
#define ALE_VERSION_1R4 0x0104

/* ALE Registers */
#define ALE_IDVER 0x00
#define ALE_STATUS 0x04
#define ALE_CONTROL 0x08
#define ALE_PRESCALE 0x10
#define ALE_UNKNOWNVLAN 0x18
#define ALE_TABLE_CONTROL 0x20
#define ALE_TABLE 0x34
#define ALE_PORTCTL 0x40

/* ALE NetCP NU switch specific Registers */
#define ALE_UNKNOWNVLAN_MEMBER 0x90
#define ALE_UNKNOWNVLAN_UNREG_MCAST_FLOOD 0x94
#define ALE_UNKNOWNVLAN_REG_MCAST_FLOOD 0x98
#define ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS 0x9C
#define ALE_VLAN_MASK_MUX(reg) (0xc0 + (0x4 * (reg)))

#define ALE_TABLE_WRITE BIT(31)

#define ALE_TYPE_FREE 0
Expand All @@ -51,6 +61,10 @@
#define ALE_UCAST_OUI 2
#define ALE_UCAST_TOUCHED 3

#define ALE_TABLE_SIZE_MULTIPLIER 1024
#define ALE_STATUS_SIZE_MASK 0x1f
#define ALE_TABLE_SIZE_DEFAULT 64

static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
{
int idx;
Expand Down Expand Up @@ -84,20 +98,34 @@ static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \
cpsw_ale_set_field(ale_entry, start, bits, value); \
}

#define DEFINE_ALE_FIELD1(name, start) \
static inline int cpsw_ale_get_##name(u32 *ale_entry, u32 bits) \
{ \
return cpsw_ale_get_field(ale_entry, start, bits); \
} \
static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value, \
u32 bits) \
{ \
cpsw_ale_set_field(ale_entry, start, bits, value); \
}

DEFINE_ALE_FIELD(entry_type, 60, 2)
DEFINE_ALE_FIELD(vlan_id, 48, 12)
DEFINE_ALE_FIELD(mcast_state, 62, 2)
DEFINE_ALE_FIELD(port_mask, 66, 3)
DEFINE_ALE_FIELD1(port_mask, 66)
DEFINE_ALE_FIELD(super, 65, 1)
DEFINE_ALE_FIELD(ucast_type, 62, 2)
DEFINE_ALE_FIELD(port_num, 66, 2)
DEFINE_ALE_FIELD1(port_num, 66)
DEFINE_ALE_FIELD(blocked, 65, 1)
DEFINE_ALE_FIELD(secure, 64, 1)
DEFINE_ALE_FIELD(vlan_untag_force, 24, 3)
DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3)
DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3)
DEFINE_ALE_FIELD(vlan_member_list, 0, 3)
DEFINE_ALE_FIELD1(vlan_untag_force, 24)
DEFINE_ALE_FIELD1(vlan_reg_mcast, 16)
DEFINE_ALE_FIELD1(vlan_unreg_mcast, 8)
DEFINE_ALE_FIELD1(vlan_member_list, 0)
DEFINE_ALE_FIELD(mcast, 40, 1)
/* ALE NetCP nu switch specific */
DEFINE_ALE_FIELD(vlan_unreg_mcast_idx, 20, 3)
DEFINE_ALE_FIELD(vlan_reg_mcast_idx, 44, 3)

/* The MAC address field in the ALE entry cannot be macroized as above */
static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
Expand Down Expand Up @@ -223,14 +251,16 @@ static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
{
int mask;

mask = cpsw_ale_get_port_mask(ale_entry);
mask = cpsw_ale_get_port_mask(ale_entry,
ale->port_mask_bits);
if ((mask & port_mask) == 0)
return; /* ports dont intersect, not interested */
mask &= ~port_mask;

/* free if only remaining port is host port */
if (mask)
cpsw_ale_set_port_mask(ale_entry, mask);
cpsw_ale_set_port_mask(ale_entry, mask,
ale->port_mask_bits);
else
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
}
Expand Down Expand Up @@ -291,7 +321,7 @@ int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
cpsw_ale_set_port_num(ale_entry, port);
cpsw_ale_set_port_num(ale_entry, port, ale->port_num_bits);

idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
if (idx < 0)
Expand Down Expand Up @@ -338,9 +368,11 @@ int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
cpsw_ale_set_mcast_state(ale_entry, mcast_state);

mask = cpsw_ale_get_port_mask(ale_entry);
mask = cpsw_ale_get_port_mask(ale_entry,
ale->port_mask_bits);
port_mask |= mask;
cpsw_ale_set_port_mask(ale_entry, port_mask);
cpsw_ale_set_port_mask(ale_entry, port_mask,
ale->port_mask_bits);

if (idx < 0)
idx = cpsw_ale_match_free(ale);
Expand All @@ -367,7 +399,8 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
cpsw_ale_read(ale, idx, ale_entry);

if (port_mask)
cpsw_ale_set_port_mask(ale_entry, port_mask);
cpsw_ale_set_port_mask(ale_entry, port_mask,
ale->port_mask_bits);
else
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);

Expand All @@ -376,6 +409,21 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
}
EXPORT_SYMBOL_GPL(cpsw_ale_del_mcast);

/* ALE NetCP NU switch specific vlan functions */
static void cpsw_ale_set_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry,
int reg_mcast, int unreg_mcast)
{
int idx;

/* Set VLAN registered multicast flood mask */
idx = cpsw_ale_get_vlan_reg_mcast_idx(ale_entry);
writel(reg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));

/* Set VLAN unregistered multicast flood mask */
idx = cpsw_ale_get_vlan_unreg_mcast_idx(ale_entry);
writel(unreg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));
}

int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
int reg_mcast, int unreg_mcast)
{
Expand All @@ -389,10 +437,16 @@ int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
cpsw_ale_set_vlan_id(ale_entry, vid);

cpsw_ale_set_vlan_untag_force(ale_entry, untag);
cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast);
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
cpsw_ale_set_vlan_member_list(ale_entry, port);
cpsw_ale_set_vlan_untag_force(ale_entry, untag, ale->vlan_field_bits);
if (!ale->params.nu_switch_ale) {
cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast,
ale->vlan_field_bits);
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast,
ale->vlan_field_bits);
} else {
cpsw_ale_set_vlan_mcast(ale, ale_entry, reg_mcast, unreg_mcast);
}
cpsw_ale_set_vlan_member_list(ale_entry, port, ale->vlan_field_bits);

if (idx < 0)
idx = cpsw_ale_match_free(ale);
Expand All @@ -418,7 +472,8 @@ int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
cpsw_ale_read(ale, idx, ale_entry);

if (port_mask)
cpsw_ale_set_vlan_member_list(ale_entry, port_mask);
cpsw_ale_set_vlan_member_list(ale_entry, port_mask,
ale->vlan_field_bits);
else
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);

Expand Down Expand Up @@ -446,12 +501,15 @@ void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
if (type != ALE_TYPE_VLAN)
continue;

unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
unreg_mcast =
cpsw_ale_get_vlan_unreg_mcast(ale_entry,
ale->vlan_field_bits);
if (allmulti)
unreg_mcast |= 1;
else
unreg_mcast &= ~1;
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast,
ale->vlan_field_bits);
cpsw_ale_write(ale, idx, ale_entry);
}
}
Expand All @@ -464,7 +522,7 @@ struct ale_control_info {
int bits;
};

static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
[ALE_ENABLE] = {
.name = "enable",
.offset = ALE_CONTROL,
Expand Down Expand Up @@ -721,11 +779,83 @@ static void cpsw_ale_timer(unsigned long arg)

void cpsw_ale_start(struct cpsw_ale *ale)
{
u32 rev;
u32 rev, ale_entries;

rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
ALE_VERSION_MAJOR(rev), ALE_VERSION_MINOR(rev));
if (!ale->params.major_ver_mask)
ale->params.major_ver_mask = 0xff;
ale->version =
(ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask) << 8) |
ALE_VERSION_MINOR(rev);
dev_info(ale->params.dev, "initialized cpsw ale version %d.%d\n",
ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask),
ALE_VERSION_MINOR(rev));

if (!ale->params.ale_entries) {
ale_entries =
__raw_readl(ale->params.ale_regs + ALE_STATUS) &
ALE_STATUS_SIZE_MASK;
/* ALE available on newer NetCP switches has introduced
* a register, ALE_STATUS, to indicate the size of ALE
* table which shows the size as a multiple of 1024 entries.
* For these, params.ale_entries will be set to zero. So
* read the register and update the value of ale_entries.
* ALE table on NetCP lite, is much smaller and is indicated
* by a value of zero in ALE_STATUS. So use a default value
* of ALE_TABLE_SIZE_DEFAULT for this. Caller is expected
* to set the value of ale_entries for all other versions
* of ALE.
*/
if (!ale_entries)
ale_entries = ALE_TABLE_SIZE_DEFAULT;
else
ale_entries *= ALE_TABLE_SIZE_MULTIPLIER;
ale->params.ale_entries = ale_entries;
}
dev_info(ale->params.dev,
"ALE Table size %ld\n", ale->params.ale_entries);

/* set default bits for existing h/w */
ale->port_mask_bits = 3;
ale->port_num_bits = 2;
ale->vlan_field_bits = 3;

/* Set defaults override for ALE on NetCP NU switch and for version
* 1R3
*/
if (ale->params.nu_switch_ale) {
/* Separate registers for unknown vlan configuration.
* Also there are N bits, where N is number of ale
* ports and shift value should be 0
*/
ale_controls[ALE_PORT_UNKNOWN_VLAN_MEMBER].bits =
ale->params.ale_ports;
ale_controls[ALE_PORT_UNKNOWN_VLAN_MEMBER].offset =
ALE_UNKNOWNVLAN_MEMBER;
ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].bits =
ale->params.ale_ports;
ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].shift = 0;
ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].offset =
ALE_UNKNOWNVLAN_UNREG_MCAST_FLOOD;
ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].bits =
ale->params.ale_ports;
ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].shift = 0;
ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].offset =
ALE_UNKNOWNVLAN_REG_MCAST_FLOOD;
ale_controls[ALE_PORT_UNTAGGED_EGRESS].bits =
ale->params.ale_ports;
ale_controls[ALE_PORT_UNTAGGED_EGRESS].shift = 0;
ale_controls[ALE_PORT_UNTAGGED_EGRESS].offset =
ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS;
ale->port_mask_bits = ale->params.ale_ports;
ale->port_num_bits = ale->params.ale_ports - 1;
ale->vlan_field_bits = ale->params.ale_ports;
} else if (ale->version == ALE_VERSION_1R3) {
ale->port_mask_bits = ale->params.ale_ports;
ale->port_num_bits = 3;
ale->vlan_field_bits = ale->params.ale_ports;
}

cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);

Expand Down
17 changes: 16 additions & 1 deletion drivers/net/ethernet/ti/cpsw_ale.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Texas Instruments 3-Port Ethernet Switch Address Lookup Engine APIs
* Texas Instruments N-Port Ethernet Switch Address Lookup Engine APIs
*
* Copyright (C) 2012 Texas Instruments
*
Expand All @@ -21,13 +21,28 @@ struct cpsw_ale_params {
unsigned long ale_ageout; /* in secs */
unsigned long ale_entries;
unsigned long ale_ports;
/* NU Switch has specific handling as number of bits in ALE entries
* are different than other versions of ALE. Also there are specific
* registers for unknown vlan specific fields. So use nu_switch_ale
* to identify this hardware.
*/
bool nu_switch_ale;
/* mask bit used in NU Switch ALE is 3 bits instead of 8 bits. So
* pass it from caller.
*/
u32 major_ver_mask;
};

struct cpsw_ale {
struct cpsw_ale_params params;
struct timer_list timer;
unsigned long ageout;
int allmulti;
u32 version;
/* These bits are different on NetCP NU Switch ALE */
u32 port_mask_bits;
u32 port_num_bits;
u32 vlan_field_bits;
};

enum cpsw_ale_control {
Expand Down
21 changes: 21 additions & 0 deletions drivers/net/ethernet/ti/netcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <linux/netdevice.h>
#include <linux/soc/ti/knav_dma.h>
#include <linux/u64_stats_sync.h>

/* Maximum Ethernet frame size supported by Keystone switch */
#define NETCP_MAX_FRAME_SIZE 9504
Expand Down Expand Up @@ -68,6 +69,20 @@ struct netcp_addr {
struct list_head node;
};

struct netcp_stats {
struct u64_stats_sync syncp_rx ____cacheline_aligned_in_smp;
u64 rx_packets;
u64 rx_bytes;
u32 rx_errors;
u32 rx_dropped;

struct u64_stats_sync syncp_tx ____cacheline_aligned_in_smp;
u64 tx_packets;
u64 tx_bytes;
u32 tx_errors;
u32 tx_dropped;
};

struct netcp_intf {
struct device *dev;
struct device *ndev_dev;
Expand All @@ -87,6 +102,11 @@ struct netcp_intf {
void *rx_fdq[KNAV_DMA_FDQ_PER_CHAN];
struct napi_struct rx_napi;
struct napi_struct tx_napi;
#define ETH_SW_CAN_REMOVE_ETH_FCS BIT(0)
u32 hw_cap;

/* 64-bit netcp stats */
struct netcp_stats stats;

void *rx_channel;
const char *dma_chan_name;
Expand Down Expand Up @@ -115,6 +135,7 @@ struct netcp_packet {
struct sk_buff *skb;
__le32 *epib;
u32 *psdata;
u32 eflags;
unsigned int psdata_len;
struct netcp_intf *netcp;
struct netcp_tx_pipe *tx_pipe;
Expand Down
Loading

0 comments on commit 82e4869

Please sign in to comment.