Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#ifndef __NET_TI_ICSS_IEP_H
#define __NET_TI_ICSS_IEP_H
#include <linux/mutex.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/regmap.h>
enum {
ICSS_IEP_GLOBAL_CFG_REG,
ICSS_IEP_GLOBAL_STATUS_REG,
ICSS_IEP_COMPEN_REG,
ICSS_IEP_SLOW_COMPEN_REG,
ICSS_IEP_COUNT_REG0,
ICSS_IEP_COUNT_REG1,
ICSS_IEP_CAPTURE_CFG_REG,
ICSS_IEP_CAPTURE_STAT_REG,
ICSS_IEP_CAP6_RISE_REG0,
ICSS_IEP_CAP6_RISE_REG1,
ICSS_IEP_CAP7_RISE_REG0,
ICSS_IEP_CAP7_RISE_REG1,
ICSS_IEP_CMP_CFG_REG,
ICSS_IEP_CMP_STAT_REG,
ICSS_IEP_CMP0_REG0,
ICSS_IEP_CMP0_REG1,
ICSS_IEP_CMP1_REG0,
ICSS_IEP_CMP1_REG1,
ICSS_IEP_CMP8_REG0,
ICSS_IEP_CMP8_REG1,
ICSS_IEP_SYNC_CTRL_REG,
ICSS_IEP_SYNC0_STAT_REG,
ICSS_IEP_SYNC1_STAT_REG,
ICSS_IEP_SYNC_PWIDTH_REG,
ICSS_IEP_SYNC0_PERIOD_REG,
ICSS_IEP_SYNC1_DELAY_REG,
ICSS_IEP_SYNC_START_REG,
ICSS_IEP_MAX_REGS,
};
/**
* struct icss_iep_plat_data - Plat data to handle SoC variants
* @config: Regmap configuration data
* @reg_offs: register offsets to capture offset differences across SoCs
* @flags: Flags to represent IEP properties
*/
struct icss_iep_plat_data {
const struct regmap_config *config;
u32 reg_offs[ICSS_IEP_MAX_REGS];
u32 flags;
};
struct icss_iep {
struct device *dev;
void __iomem *base;
const struct icss_iep_plat_data *plat_data;
struct regmap *map;
struct device_node *client_np;
unsigned long refclk_freq;
int clk_tick_time; /* one refclk tick time in ns */
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp_clock;
struct mutex ptp_clk_mutex; /* PHC access serializer */
u32 def_inc;
s16 slow_cmp_inc;
u32 slow_cmp_count;
const struct icss_iep_clockops *ops;
void *clockops_data;
u32 cycle_time_ns;
u32 perout_enabled;
bool pps_enabled;
int cap_cmp_irq;
u64 period;
u32 latch_enable;
struct work_struct work;
};
extern const struct icss_iep_clockops prueth_iep_clockops;
/* Firmware specific clock operations */
struct icss_iep_clockops {
void (*settime)(void *clockops_data, u64 ns);
void (*adjtime)(void *clockops_data, s64 delta);
u64 (*gettime)(void *clockops_data, struct ptp_system_timestamp *sts);
int (*perout_enable)(void *clockops_data,
struct ptp_perout_request *req, int on,
u64 *cmp);
int (*extts_enable)(void *clockops_data, u32 index, int on);
};
struct icss_iep *icss_iep_get(struct device_node *np);
struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx);
void icss_iep_put(struct icss_iep *iep);
int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,
void *clockops_data, u32 cycle_time_ns);
int icss_iep_exit(struct icss_iep *iep);
int icss_iep_get_count_low(struct icss_iep *iep);
int icss_iep_get_count_hi(struct icss_iep *iep);
int icss_iep_get_ptp_clock_idx(struct icss_iep *iep);
void icss_iep_init_fw(struct icss_iep *iep);
void icss_iep_exit_fw(struct icss_iep *iep);
#endif /* __NET_TI_ICSS_IEP_H */
@@ -0,0 +1,534 @@
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments ICSSG Ethernet Driver
*
* Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#include <linux/etherdevice.h>
#include <linux/types.h>
#include <linux/regmap.h>
#include "icssg_prueth.h"
#define ICSSG_NUM_CLASSIFIERS 16
#define ICSSG_NUM_FT1_SLOTS 8
#define ICSSG_NUM_FT3_SLOTS 16
#define ICSSG_NUM_CLASSIFIERS_IN_USE 5
/* Filter 1 - FT1 */
#define FT1_NUM_SLOTS 8
#define FT1_SLOT_SIZE 0x10 /* bytes */
/* offsets from FT1 slot base i.e. slot 1 start */
#define FT1_DA0 0x0
#define FT1_DA1 0x4
#define FT1_DA0_MASK 0x8
#define FT1_DA1_MASK 0xc
#define FT1_N_REG(slice, n, reg) \
(offs[slice].ft1_slot_base + FT1_SLOT_SIZE * (n) + (reg))
#define FT1_LEN_MASK GENMASK(19, 16)
#define FT1_LEN_SHIFT 16
#define FT1_LEN(len) (((len) << FT1_LEN_SHIFT) & FT1_LEN_MASK)
#define FT1_START_MASK GENMASK(14, 0)
#define FT1_START(start) ((start) & FT1_START_MASK)
#define FT1_MATCH_SLOT(n) (GENMASK(23, 16) & (BIT(n) << 16))
/* FT1 config type */
enum ft1_cfg_type {
FT1_CFG_TYPE_DISABLED = 0,
FT1_CFG_TYPE_EQ,
FT1_CFG_TYPE_GT,
FT1_CFG_TYPE_LT,
};
#define FT1_CFG_SHIFT(n) (2 * (n))
#define FT1_CFG_MASK(n) (0x3 << FT1_CFG_SHIFT((n)))
/* Filter 3 - FT3 */
#define FT3_NUM_SLOTS 16
#define FT3_SLOT_SIZE 0x20 /* bytes */
/* offsets from FT3 slot n's base */
#define FT3_START 0
#define FT3_START_AUTO 0x4
#define FT3_START_OFFSET 0x8
#define FT3_JUMP_OFFSET 0xc
#define FT3_LEN 0x10
#define FT3_CFG 0x14
#define FT3_T 0x18
#define FT3_T_MASK 0x1c
#define FT3_N_REG(slice, n, reg) \
(offs[slice].ft3_slot_base + FT3_SLOT_SIZE * (n) + (reg))
/* offsets from rx_class n's base */
#define RX_CLASS_AND_EN 0
#define RX_CLASS_OR_EN 0x4
#define RX_CLASS_NUM_SLOTS 16
#define RX_CLASS_EN_SIZE 0x8 /* bytes */
#define RX_CLASS_N_REG(slice, n, reg) \
(offs[slice].rx_class_base + RX_CLASS_EN_SIZE * (n) + (reg))
#define RX_CLASS_OR_REG(slice, n, reg) \
(offs[slice].rx_class_or_base + RX_CLASS_EN_SIZE * (n) + (reg))
/* RX Class Gates */
#define RX_CLASS_GATES_SIZE 0x4 /* bytes */
#define RX_CLASS_GATES_N_REG(slice, n) \
(offs[slice].rx_class_gates_base + RX_CLASS_GATES_SIZE * (n))
#define RX_CLASS_GATES_ALLOW_MASK BIT(6)
#define RX_CLASS_GATES_RAW_MASK BIT(5)
#define RX_CLASS_GATES_PHASE_MASK BIT(4)
/* RX Class traffic data matching bits */
#define RX_CLASS_FT_UC BIT(31)
#define RX_CLASS_FT_MC BIT(30)
#define RX_CLASS_FT_BC BIT(29)
#define RX_CLASS_FT_FW BIT(28)
#define RX_CLASS_FT_RCV BIT(27)
#define RX_CLASS_FT_VLAN BIT(26)
#define RX_CLASS_FT_DA_P BIT(25)
#define RX_CLASS_FT_DA_I BIT(24)
#define RX_CLASS_FT_FT1_MATCH_MASK GENMASK(23, 16)
#define RX_CLASS_FT_FT1_MATCH_SHIFT 16
#define RX_CLASS_FT_FT3_MATCH_MASK GENMASK(15, 0)
#define RX_CLASS_FT_FT3_MATCH_SHIFT 0
#define RX_CLASS_FT_FT1_MATCH(slot) \
((BIT(slot) << RX_CLASS_FT_FT1_MATCH_SHIFT) & \
RX_CLASS_FT_FT1_MATCH_MASK)
/* HSR/PRP protocol frame filtering */
#define HSR_PTP_ETHERTYPE_OFFSET 0x12
#define PRP_PTP_ETHERTYPE_OFFSET 0x0C
#define FT3_PTP_PATTERN 0xF788
#define FT3_ETHERTYPE_MASK 0xFFFF0000U
#define FT3_VLAN_MODE_BOTH 0x1
#define RX_CLASS_OR_DUP_PTP 0x4200
#define RX_CLASS_OR_HSR_TAG 0x4000
#define RX_CLASS_GATE_PTP 0x50
#define RX_CLASS_DISABLE 0x0
/* HSR/PRP classifier indices */
#define CLASSIFIER_PTP_DUP 10
#define CLASSIFIER_HSR_TAG 11
#define FT3_PTP_SLOT 14
/* RX class type */
enum rx_class_sel_type {
RX_CLASS_SEL_TYPE_OR = 0,
RX_CLASS_SEL_TYPE_AND = 1,
RX_CLASS_SEL_TYPE_OR_AND_AND = 2,
RX_CLASS_SEL_TYPE_OR_OR_AND = 3,
};
#define FT1_CFG_SHIFT(n) (2 * (n))
#define FT1_CFG_MASK(n) (0x3 << FT1_CFG_SHIFT((n)))
#define RX_CLASS_SEL_SHIFT(n) (2 * (n))
#define RX_CLASS_SEL_MASK(n) (0x3 << RX_CLASS_SEL_SHIFT((n)))
#define ICSSG_CFG_OFFSET 0
#define MAC_INTERFACE_0 0x18
#define MAC_INTERFACE_1 0x1c
#define ICSSG_CFG_RX_L2_G_EN BIT(2)
/* These are register offsets per PRU */
struct miig_rt_offsets {
u32 mac0;
u32 mac1;
u32 ft1_start_len;
u32 ft1_cfg;
u32 ft1_slot_base;
u32 ft3_slot_base;
u32 ft3_p_base;
u32 ft_rx_ptr;
u32 rx_class_base;
u32 rx_class_or_base;
u32 rx_class_cfg1;
u32 rx_class_cfg2;
u32 rx_class_gates_base;
u32 rx_green;
u32 rx_rate_cfg_base;
u32 rx_rate_src_sel0;
u32 rx_rate_src_sel1;
u32 tx_rate_cfg_base;
u32 stat_base;
u32 tx_hsr_tag;
u32 tx_hsr_seq;
u32 tx_vlan_type;
u32 tx_vlan_ins;
};
/* These are the offset values for miig_rt_offsets registers */
static const struct miig_rt_offsets offs[] = {
/* PRU0 */
{
0x8,
0xc,
0x80,
0x84,
0x88,
0x108,
0x308,
0x408,
0x40c,
0x410,
0x48c,
0x490,
0x494,
0x4d4,
0x4e4,
0x504,
0x508,
0x50c,
0x54c,
0x63c,
0x640,
0x644,
0x648,
},
/* PRU1 */
{
0x10,
0x14,
0x64c,
0x650,
0x654,
0x6d4,
0x8d4,
0x9d4,
0x9d8,
0x9dc,
0xa58,
0xa5c,
0xa60,
0xaa0,
0xab0,
0xad0,
0xad4,
0xad8,
0xb18,
0xc08,
0xc0c,
0xc10,
0xc14,
},
};
static void rx_class_ft1_set_start_len(struct regmap *miig_rt, int slice,
u16 start, u8 len)
{
u32 offset, val;
offset = offs[slice].ft1_start_len;
val = FT1_LEN(len) | FT1_START(start);
regmap_write(miig_rt, offset, val);
}
static void rx_class_ft1_set_da(struct regmap *miig_rt, int slice,
int n, const u8 *addr)
{
u32 offset;
offset = FT1_N_REG(slice, n, FT1_DA0);
regmap_write(miig_rt, offset, (u32)(addr[0] | addr[1] << 8 |
addr[2] << 16 | addr[3] << 24));
offset = FT1_N_REG(slice, n, FT1_DA1);
regmap_write(miig_rt, offset, (u32)(addr[4] | addr[5] << 8));
}
static void rx_class_ft1_set_da_mask(struct regmap *miig_rt, int slice,
int n, const u8 *addr)
{
u32 offset;
offset = FT1_N_REG(slice, n, FT1_DA0_MASK);
regmap_write(miig_rt, offset, (u32)(addr[0] | addr[1] << 8 |
addr[2] << 16 | addr[3] << 24));
offset = FT1_N_REG(slice, n, FT1_DA1_MASK);
regmap_write(miig_rt, offset, (u32)(addr[4] | addr[5] << 8));
}
static void rx_class_ft1_cfg_set_type(struct regmap *miig_rt, int slice, int n,
enum ft1_cfg_type type)
{
u32 offset;
offset = offs[slice].ft1_cfg;
regmap_update_bits(miig_rt, offset, FT1_CFG_MASK(n),
type << FT1_CFG_SHIFT(n));
}
static void rx_class_sel_set_type(struct regmap *miig_rt, int slice, int n,
enum rx_class_sel_type type)
{
u32 offset;
offset = offs[slice].rx_class_cfg1;
regmap_update_bits(miig_rt, offset, RX_CLASS_SEL_MASK(n),
type << RX_CLASS_SEL_SHIFT(n));
}
static void rx_class_set_and(struct regmap *miig_rt, int slice, int n,
u32 data)
{
u32 offset;
offset = RX_CLASS_N_REG(slice, n, RX_CLASS_AND_EN);
regmap_write(miig_rt, offset, data);
}
static void rx_class_set_or(struct regmap *miig_rt, int slice, int n,
u32 data)
{
u32 offset;
offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN);
regmap_write(miig_rt, offset, data);
}
static u32 rx_class_get_or(struct regmap *miig_rt, int slice, int n)
{
u32 offset, val;
offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN);
regmap_read(miig_rt, offset, &val);
return val;
}
void icssg_class_set_host_mac_addr(struct regmap *miig_rt, const u8 *mac)
{
regmap_write(miig_rt, MAC_INTERFACE_0, (u32)(mac[0] | mac[1] << 8 |
mac[2] << 16 | mac[3] << 24));
regmap_write(miig_rt, MAC_INTERFACE_1, (u32)(mac[4] | mac[5] << 8));
}
EXPORT_SYMBOL_GPL(icssg_class_set_host_mac_addr);
void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac)
{
regmap_write(miig_rt, offs[slice].mac0, (u32)(mac[0] | mac[1] << 8 |
mac[2] << 16 | mac[3] << 24));
regmap_write(miig_rt, offs[slice].mac1, (u32)(mac[4] | mac[5] << 8));
}
EXPORT_SYMBOL_GPL(icssg_class_set_mac_addr);
static void icssg_class_ft1_add_mcast(struct regmap *miig_rt, int slice,
int slot, const u8 *addr, const u8 *mask)
{
u32 val;
int i;
WARN(slot >= FT1_NUM_SLOTS, "invalid slot: %d\n", slot);
rx_class_ft1_set_da(miig_rt, slice, slot, addr);
rx_class_ft1_set_da_mask(miig_rt, slice, slot, mask);
rx_class_ft1_cfg_set_type(miig_rt, slice, slot, FT1_CFG_TYPE_EQ);
/* Enable the FT1 slot in OR enable for all classifiers */
for (i = 0; i < ICSSG_NUM_CLASSIFIERS_IN_USE; i++) {
val = rx_class_get_or(miig_rt, slice, i);
val |= RX_CLASS_FT_FT1_MATCH(slot);
rx_class_set_or(miig_rt, slice, i, val);
}
}
/* disable all RX traffic */
void icssg_class_disable(struct regmap *miig_rt, int slice)
{
u32 data, offset;
int n;
/* Enable RX_L2_G */
regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, ICSSG_CFG_RX_L2_G_EN,
ICSSG_CFG_RX_L2_G_EN);
for (n = 0; n < ICSSG_NUM_CLASSIFIERS; n++) {
/* AND_EN = 0 */
rx_class_set_and(miig_rt, slice, n, 0);
/* OR_EN = 0 */
rx_class_set_or(miig_rt, slice, n, 0);
/* set CFG1 to OR */
rx_class_sel_set_type(miig_rt, slice, n, RX_CLASS_SEL_TYPE_OR);
/* configure gate */
offset = RX_CLASS_GATES_N_REG(slice, n);
regmap_read(miig_rt, offset, &data);
/* clear class_raw so we go through filters */
data &= ~RX_CLASS_GATES_RAW_MASK;
/* set allow and phase mask */
data |= RX_CLASS_GATES_ALLOW_MASK | RX_CLASS_GATES_PHASE_MASK;
regmap_write(miig_rt, offset, data);
}
/* FT1 Disabled */
for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++) {
const u8 addr[] = { 0, 0, 0, 0, 0, 0, };
rx_class_ft1_cfg_set_type(miig_rt, slice, n,
FT1_CFG_TYPE_DISABLED);
rx_class_ft1_set_da(miig_rt, slice, n, addr);
rx_class_ft1_set_da_mask(miig_rt, slice, n, addr);
}
/* clear CFG2 */
regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0);
}
EXPORT_SYMBOL_GPL(icssg_class_disable);
void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti,
bool is_sr1)
{
int num_classifiers = is_sr1 ? ICSSG_NUM_CLASSIFIERS_IN_USE : 1;
u32 data;
int n;
/* defaults */
icssg_class_disable(miig_rt, slice);
/* Setup Classifier */
for (n = 0; n < num_classifiers; n++) {
/* match on Broadcast or MAC_PRU address */
data = RX_CLASS_FT_BC | RX_CLASS_FT_DA_P;
/* multicast */
if (allmulti)
data |= RX_CLASS_FT_MC;
rx_class_set_or(miig_rt, slice, n, data);
/* set CFG1 for OR_OR_AND for classifier */
rx_class_sel_set_type(miig_rt, slice, n,
RX_CLASS_SEL_TYPE_OR_OR_AND);
}
/* clear CFG2 */
regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0);
}
EXPORT_SYMBOL_GPL(icssg_class_default);
void icssg_class_promiscuous_sr1(struct regmap *miig_rt, int slice)
{
u32 data, offset;
int n;
/* defaults */
icssg_class_disable(miig_rt, slice);
/* Setup Classifier */
for (n = 0; n < ICSSG_NUM_CLASSIFIERS_IN_USE; n++) {
/* set RAW_MASK to bypass filters */
offset = RX_CLASS_GATES_N_REG(slice, n);
regmap_read(miig_rt, offset, &data);
data |= RX_CLASS_GATES_RAW_MASK;
regmap_write(miig_rt, offset, data);
}
}
EXPORT_SYMBOL_GPL(icssg_class_promiscuous_sr1);
void icssg_class_add_mcast_sr1(struct regmap *miig_rt, int slice,
struct net_device *ndev)
{
u8 mask_addr[6] = { 0, 0, 0, 0, 0, 0xff };
struct netdev_hw_addr *ha;
int slot = 2;
rx_class_ft1_set_start_len(miig_rt, slice, 0, 6);
/* reserve first 2 slots for
* 1) 01-80-C2-00-00-XX Known Service Ethernet Multicast addresses
* 2) 01-00-5e-00-00-XX Local Network Control Block
* (224.0.0.0 - 224.0.0.255 (224.0.0/24))
*/
icssg_class_ft1_add_mcast(miig_rt, slice, 0,
eth_reserved_addr_base, mask_addr);
icssg_class_ft1_add_mcast(miig_rt, slice, 1,
eth_ipv4_mcast_addr_base, mask_addr);
mask_addr[5] = 0;
netdev_for_each_mc_addr(ha, ndev) {
/* skip addresses matching reserved slots */
if (!memcmp(eth_reserved_addr_base, ha->addr, 5) ||
!memcmp(eth_ipv4_mcast_addr_base, ha->addr, 5)) {
netdev_dbg(ndev, "mcast skip %pM\n", ha->addr);
continue;
}
if (slot >= FT1_NUM_SLOTS) {
netdev_dbg(ndev,
"can't add more than %d MC addresses, enabling allmulti\n",
FT1_NUM_SLOTS);
icssg_class_default(miig_rt, slice, 1, true);
break;
}
netdev_dbg(ndev, "mcast add %pM\n", ha->addr);
icssg_class_ft1_add_mcast(miig_rt, slice, slot,
ha->addr, mask_addr);
slot++;
}
}
EXPORT_SYMBOL_GPL(icssg_class_add_mcast_sr1);
/* required for SAV check */
void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr)
{
const u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, };
rx_class_ft1_set_start_len(miig_rt, slice, ETH_ALEN, ETH_ALEN);
rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr);
rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr);
rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ);
}
EXPORT_SYMBOL_GPL(icssg_ft1_set_mac_addr);
/**
* icssg_ft3_hsr_configurations - Configure filter table for HSR/PRP protocol frames
* @miig_rt: Pointer to the MII-G register map
* @slice: ICSSG slice number (0 or 1)
* @prueth: Pointer to prueth structure to determine HSR/PRP mode
*
* Configures FT3 to detect PTP frames (EtherType 0x88F7) in HSR/PRP tagged packets.
* HSR frames have a 6-byte tag, while PRP has no tag offset for EtherType detection.
*/
void icssg_ft3_hsr_configurations(struct regmap *miig_rt, int slice,
struct prueth *prueth)
{
u8 offset = (prueth->hsr_prp_version == PRP_V1) ?
PRP_PTP_ETHERTYPE_OFFSET : HSR_PTP_ETHERTYPE_OFFSET;
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_START), offset);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_START_AUTO), 0);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_START_OFFSET), 0);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_JUMP_OFFSET), 0);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_LEN), 0);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_CFG), FT3_VLAN_MODE_BOTH);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_T), FT3_PTP_PATTERN);
regmap_write(miig_rt, FT3_N_REG(slice, FT3_PTP_SLOT, FT3_T_MASK),
FT3_ETHERTYPE_MASK);
regmap_write(miig_rt, RX_CLASS_N_REG(slice, CLASSIFIER_PTP_DUP, RX_CLASS_AND_EN),
RX_CLASS_DISABLE);
regmap_write(miig_rt, RX_CLASS_OR_REG(slice, CLASSIFIER_PTP_DUP, RX_CLASS_AND_EN),
RX_CLASS_OR_DUP_PTP);
regmap_write(miig_rt, RX_CLASS_GATES_N_REG(slice, CLASSIFIER_PTP_DUP),
RX_CLASS_GATE_PTP);
if (prueth->hsr_prp_version != PRP_V1) {
regmap_write(miig_rt, RX_CLASS_N_REG(slice, CLASSIFIER_HSR_TAG, RX_CLASS_AND_EN),
RX_CLASS_DISABLE);
regmap_write(miig_rt, RX_CLASS_OR_REG(slice, CLASSIFIER_HSR_TAG, RX_CLASS_AND_EN),
RX_CLASS_OR_HSR_TAG);
regmap_write(miig_rt, RX_CLASS_GATES_N_REG(slice, CLASSIFIER_HSR_TAG),
RX_CLASS_GATE_PTP);
}
}
EXPORT_SYMBOL_GPL(icssg_ft3_hsr_configurations);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,864 @@
// SPDX-License-Identifier: GPL-2.0
/* ICSSG Ethernet driver
*
* Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com
*/
#include <linux/iopoll.h>
#include <linux/regmap.h>
#include <uapi/linux/if_ether.h>
#include "icssg_config.h"
#include "icssg_prueth.h"
#include "icssg_switch_map.h"
#include "icssg_mii_rt.h"
/* TX IPG Values to be set for 100M link speed. These values are
* in ocp_clk cycles. So need change if ocp_clk is changed for a specific
* h/w design.
*/
/* IPG is in core_clk cycles */
#define MII_RT_TX_IPG_100M 0x17
#define MII_RT_TX_IPG_1G 0xb
#define MII_RT_TX_IPG_100M_SR1 0x166
#define MII_RT_TX_IPG_1G_SR1 0x1a
#define ICSSG_QUEUES_MAX 64
#define ICSSG_QUEUE_OFFSET 0xd00
#define ICSSG_QUEUE_PEEK_OFFSET 0xe00
#define ICSSG_QUEUE_CNT_OFFSET 0xe40
#define ICSSG_QUEUE_RESET_OFFSET 0xf40
#define ICSSG_NUM_TX_QUEUES 8
#define RECYCLE_Q_SLICE0 16
#define RECYCLE_Q_SLICE1 17
#define ICSSG_NUM_OTHER_QUEUES 5 /* port, host and special queues */
#define PORT_HI_Q_SLICE0 32
#define PORT_LO_Q_SLICE0 33
#define HOST_HI_Q_SLICE0 34
#define HOST_LO_Q_SLICE0 35
#define HOST_SPL_Q_SLICE0 40 /* Special Queue */
#define PORT_HI_Q_SLICE1 36
#define PORT_LO_Q_SLICE1 37
#define HOST_HI_Q_SLICE1 38
#define HOST_LO_Q_SLICE1 39
#define HOST_SPL_Q_SLICE1 41 /* Special Queue */
#define MII_RXCFG_DEFAULT (PRUSS_MII_RT_RXCFG_RX_ENABLE | \
PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | \
PRUSS_MII_RT_RXCFG_RX_L2_EN | \
PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS)
#define MII_TXCFG_DEFAULT (PRUSS_MII_RT_TXCFG_TX_ENABLE | \
PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | \
PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | \
PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN)
#define ICSSG_CFG_DEFAULT (ICSSG_CFG_TX_L1_EN | \
ICSSG_CFG_TX_L2_EN | ICSSG_CFG_RX_L2_G_EN | \
ICSSG_CFG_TX_PRU_EN | \
ICSSG_CFG_SGMII_MODE)
#define FDB_GEN_CFG1 0x60
#define SMEM_VLAN_OFFSET 8
#define SMEM_VLAN_OFFSET_MASK GENMASK(25, 8)
#define FDB_HASH_SIZE_MASK GENMASK(6, 3)
#define FDB_HASH_SIZE_SHIFT 3
#define FDB_HASH_SIZE 3
#define FDB_GEN_CFG2 0x64
#define FDB_VLAN_EN BIT(6)
#define FDB_HOST_EN BIT(2)
#define FDB_PRU1_EN BIT(1)
#define FDB_PRU0_EN BIT(0)
#define FDB_EN_ALL (FDB_PRU0_EN | FDB_PRU1_EN | \
FDB_HOST_EN | FDB_VLAN_EN)
/**
* struct map - ICSSG Queue Map
* @queue: Queue number
* @pd_addr_start: Packet descriptor queue reserved memory
* @flags: Flags
* @special: Indicates whether this queue is a special queue or not
*/
struct map {
int queue;
u32 pd_addr_start;
u32 flags;
bool special;
};
/* Hardware queue map for ICSSG */
static const struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = {
{
{ PORT_HI_Q_SLICE0, PORT_DESC0_HI, 0x200000, 0 },
{ PORT_LO_Q_SLICE0, PORT_DESC0_LO, 0, 0 },
{ HOST_HI_Q_SLICE0, HOST_DESC0_HI, 0x200000, 0 },
{ HOST_LO_Q_SLICE0, HOST_DESC0_LO, 0, 0 },
{ HOST_SPL_Q_SLICE0, HOST_SPPD0, 0x400000, 1 },
},
{
{ PORT_HI_Q_SLICE1, PORT_DESC1_HI, 0xa00000, 0 },
{ PORT_LO_Q_SLICE1, PORT_DESC1_LO, 0x800000, 0 },
{ HOST_HI_Q_SLICE1, HOST_DESC1_HI, 0xa00000, 0 },
{ HOST_LO_Q_SLICE1, HOST_DESC1_LO, 0x800000, 0 },
{ HOST_SPL_Q_SLICE1, HOST_SPPD1, 0xc00000, 1 },
},
};
static void icssg_config_mii_init_fw_offload(struct prueth_emac *emac)
{
struct prueth *prueth = emac->prueth;
int mii = prueth_emac_slice(emac);
u32 txcfg_reg, pcnt_reg, txcfg;
struct regmap *mii_rt;
mii_rt = prueth->mii_rt;
txcfg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
PRUSS_MII_RT_TXCFG1;
pcnt_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
PRUSS_MII_RT_RX_PCNT1;
txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE |
PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE |
PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN;
if (emac->phy_if == PHY_INTERFACE_MODE_MII && mii == ICSS_MII1)
txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
else if (emac->phy_if != PHY_INTERFACE_MODE_MII && mii == ICSS_MII0)
txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
regmap_write(mii_rt, txcfg_reg, txcfg);
regmap_write(mii_rt, pcnt_reg, 0x1);
}
static void icssg_config_mii_init(struct prueth_emac *emac)
{
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
u32 txcfg, txcfg_reg, pcnt_reg;
struct regmap *mii_rt;
mii_rt = prueth->mii_rt;
txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
PRUSS_MII_RT_TXCFG1;
pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
PRUSS_MII_RT_RX_PCNT1;
txcfg = MII_TXCFG_DEFAULT;
/* In MII mode TX lines swapped inside ICSSG, so TX_MUX_SEL cfg need
* to be swapped also comparing to RGMII mode.
*/
if (emac->phy_if == PHY_INTERFACE_MODE_MII && slice == ICSS_MII0)
txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
else if (emac->phy_if != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1)
txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
regmap_write(mii_rt, txcfg_reg, txcfg);
regmap_write(mii_rt, pcnt_reg, 0x1);
}
static void icssg_miig_queues_init(struct prueth *prueth, int slice)
{
struct regmap *miig_rt = prueth->miig_rt;
void __iomem *smem = prueth->shram.va;
u8 pd[ICSSG_SPECIAL_PD_SIZE];
int queue = 0, i, j;
u32 *pdword;
/* reset hwqueues */
if (slice)
queue = ICSSG_NUM_TX_QUEUES;
for (i = 0; i < ICSSG_NUM_TX_QUEUES; i++) {
regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue);
queue++;
}
queue = slice ? RECYCLE_Q_SLICE1 : RECYCLE_Q_SLICE0;
regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue);
for (i = 0; i < ICSSG_NUM_OTHER_QUEUES; i++) {
regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET,
hwq_map[slice][i].queue);
}
/* initialize packet descriptors in SMEM */
/* push pakcet descriptors to hwqueues */
pdword = (u32 *)pd;
for (j = 0; j < ICSSG_NUM_OTHER_QUEUES; j++) {
const struct map *mp;
int pd_size, num_pds;
u32 pdaddr;
mp = &hwq_map[slice][j];
if (mp->special) {
pd_size = ICSSG_SPECIAL_PD_SIZE;
num_pds = ICSSG_NUM_SPECIAL_PDS;
} else {
pd_size = ICSSG_NORMAL_PD_SIZE;
num_pds = ICSSG_NUM_NORMAL_PDS;
}
for (i = 0; i < num_pds; i++) {
memset(pd, 0, pd_size);
pdword[0] &= ICSSG_FLAG_MASK;
pdword[0] |= mp->flags;
pdaddr = mp->pd_addr_start + i * pd_size;
memcpy_toio(smem + pdaddr, pd, pd_size);
queue = mp->queue;
regmap_write(miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue,
pdaddr);
}
}
}
void icssg_config_ipg(struct prueth_emac *emac)
{
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
u32 ipg;
switch (emac->speed) {
case SPEED_1000:
ipg = emac->is_sr1 ? MII_RT_TX_IPG_1G_SR1 : MII_RT_TX_IPG_1G;
break;
case SPEED_100:
ipg = emac->is_sr1 ? MII_RT_TX_IPG_100M_SR1 : MII_RT_TX_IPG_100M;
break;
case SPEED_10:
/* Firmware hardcodes IPG for SR1.0 */
if (emac->is_sr1)
return;
/* IPG for 10M is same as 100M */
ipg = MII_RT_TX_IPG_100M;
break;
default:
/* Other links speeds not supported */
netdev_err(emac->ndev, "Unsupported link speed\n");
return;
}
icssg_mii_update_ipg(prueth->mii_rt, slice, ipg);
}
EXPORT_SYMBOL_GPL(icssg_config_ipg);
static void emac_r30_cmd_init(struct prueth_emac *emac)
{
struct icssg_r30_cmd __iomem *p;
int i;
p = emac->dram.va + MGR_R30_CMD_OFFSET;
for (i = 0; i < 4; i++)
writel(EMAC_NONE, &p->cmd[i]);
}
static int emac_r30_is_done(struct prueth_emac *emac)
{
const struct icssg_r30_cmd __iomem *p;
u32 cmd;
int i;
p = emac->dram.va + MGR_R30_CMD_OFFSET;
for (i = 0; i < 4; i++) {
cmd = readl(&p->cmd[i]);
if (cmd != EMAC_NONE)
return 0;
}
return 1;
}
static int prueth_fw_offload_buffer_setup(struct prueth_emac *emac)
{
struct icssg_buffer_pool_cfg __iomem *bpool_cfg;
struct icssg_rxq_ctx __iomem *rxq_ctx;
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
u32 addr;
int i;
addr = lower_32_bits(prueth->msmcram.pa);
if (slice) {
if (prueth->pdata.banked_ms_ram)
addr += MSMC_RAM_BANK_SIZE;
else
addr += PRUETH_SW_TOTAL_BUF_SIZE_PER_SLICE;
}
if (addr % SZ_64K) {
dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n");
return -EINVAL;
}
bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET;
/* Configure buffer pools for forwarding buffers
* - used by firmware to store packets to be forwarded to other port
* - 8 total pools per slice
*/
for (i = 0; i < PRUETH_NUM_FWD_BUF_POOLS_PER_SLICE; i++) {
writel(addr, &bpool_cfg[i].addr);
writel(PRUETH_SW_FWD_BUF_POOL_SIZE, &bpool_cfg[i].len);
addr += PRUETH_SW_FWD_BUF_POOL_SIZE;
}
/* Configure buffer pools for Local Injection buffers
* - used by firmware to store packets received from host core
* - 16 total pools per slice
*/
for (i = 0; i < PRUETH_NUM_LI_BUF_POOLS_PER_SLICE; i++) {
int cfg_idx = i + PRUETH_NUM_FWD_BUF_POOLS_PER_SLICE;
/* The driver only uses first 4 queues per PRU,
* so only initialize buffer for them
*/
if ((i % PRUETH_NUM_LI_BUF_POOLS_PER_PORT_PER_SLICE)
< PRUETH_SW_USED_LI_BUF_POOLS_PER_PORT_PER_SLICE) {
writel(addr, &bpool_cfg[cfg_idx].addr);
writel(PRUETH_SW_LI_BUF_POOL_SIZE,
&bpool_cfg[cfg_idx].len);
addr += PRUETH_SW_LI_BUF_POOL_SIZE;
} else {
writel(0, &bpool_cfg[cfg_idx].addr);
writel(0, &bpool_cfg[cfg_idx].len);
}
}
/* Express RX buffer queue
* - used by firmware to store express packets to be transmitted
* to the host core
*/
rxq_ctx = emac->dram.va + HOST_RX_Q_EXP_CONTEXT_OFFSET;
for (i = 0; i < 3; i++)
writel(addr, &rxq_ctx->start[i]);
addr += PRUETH_SW_HOST_EXP_BUF_POOL_SIZE;
writel(addr, &rxq_ctx->end);
/* Pre-emptible RX buffer queue
* - used by firmware to store preemptible packets to be transmitted
* to the host core
*/
rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET;
for (i = 0; i < 3; i++)
writel(addr, &rxq_ctx->start[i]);
addr += PRUETH_SW_HOST_PRE_BUF_POOL_SIZE;
writel(addr, &rxq_ctx->end);
/* Set pointer for default dropped packet write
* - used by firmware to temporarily store packet to be dropped
*/
rxq_ctx = emac->dram.va + DEFAULT_MSMC_Q_OFFSET;
writel(addr, &rxq_ctx->start[0]);
return 0;
}
static int prueth_emac_buffer_setup(struct prueth_emac *emac)
{
struct icssg_buffer_pool_cfg __iomem *bpool_cfg;
struct icssg_rxq_ctx __iomem *rxq_ctx;
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
u32 addr;
int i;
addr = lower_32_bits(prueth->msmcram.pa);
if (slice) {
if (prueth->pdata.banked_ms_ram)
addr += MSMC_RAM_BANK_SIZE;
else
addr += PRUETH_EMAC_TOTAL_BUF_SIZE_PER_SLICE;
}
if (addr % SZ_64K) {
dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n");
return -EINVAL;
}
bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET;
/* Configure buffer pools for forwarding buffers
* - in mac mode - no forwarding so initialize all pools to 0
* - 8 total pools per slice
*/
for (i = 0; i < PRUETH_NUM_FWD_BUF_POOLS_PER_SLICE; i++) {
writel(0, &bpool_cfg[i].addr);
writel(0, &bpool_cfg[i].len);
}
/* Configure buffer pools for Local Injection buffers
* - used by firmware to store packets received from host core
* - 16 total pools per slice
*/
bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET;
for (i = 0; i < PRUETH_NUM_LI_BUF_POOLS_PER_SLICE; i++) {
int cfg_idx = i + PRUETH_NUM_FWD_BUF_POOLS_PER_SLICE;
/* In EMAC mode, only first 4 buffers are used,
* as 1 slice needs to handle only 1 port
*/
if (i < PRUETH_EMAC_USED_LI_BUF_POOLS_PER_PORT_PER_SLICE) {
writel(addr, &bpool_cfg[cfg_idx].addr);
writel(PRUETH_EMAC_LI_BUF_POOL_SIZE,
&bpool_cfg[cfg_idx].len);
addr += PRUETH_EMAC_LI_BUF_POOL_SIZE;
} else {
writel(0, &bpool_cfg[cfg_idx].addr);
writel(0, &bpool_cfg[cfg_idx].len);
}
}
/* Express RX buffer queue
* - used by firmware to store express packets to be transmitted
* to host core
*/
rxq_ctx = emac->dram.va + HOST_RX_Q_EXP_CONTEXT_OFFSET;
for (i = 0; i < 3; i++)
writel(addr, &rxq_ctx->start[i]);
addr += PRUETH_EMAC_HOST_EXP_BUF_POOL_SIZE;
writel(addr, &rxq_ctx->end);
/* Pre-emptible RX buffer queue
* - used by firmware to store preemptible packets to be transmitted
* to host core
*/
rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET;
for (i = 0; i < 3; i++)
writel(addr, &rxq_ctx->start[i]);
addr += PRUETH_EMAC_HOST_PRE_BUF_POOL_SIZE;
writel(addr, &rxq_ctx->end);
/* Set pointer for default dropped packet write
* - used by firmware to temporarily store packet to be dropped
*/
rxq_ctx = emac->dram.va + DEFAULT_MSMC_Q_OFFSET;
writel(addr, &rxq_ctx->start[0]);
return 0;
}
void icssg_init_emac_mode(struct prueth *prueth)
{
/* When the device is configured as a bridge and it is being brought
* back to the emac mode, the host mac address has to be set as 0.
*/
u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET;
int i;
u8 mac[ETH_ALEN] = { 0 };
/* Set VLAN TABLE address base */
regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
addr << SMEM_VLAN_OFFSET);
regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, FDB_HASH_SIZE_MASK,
FDB_HASH_SIZE << FDB_HASH_SIZE_SHIFT);
/* Set enable VLAN aware mode, and FDBs for all PRUs */
regmap_write(prueth->miig_rt, FDB_GEN_CFG2, (FDB_PRU0_EN | FDB_PRU1_EN | FDB_HOST_EN));
prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va +
EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET);
for (i = 0; i < SZ_4K - 1; i++) {
prueth->vlan_tbl[i].fid = i;
prueth->vlan_tbl[i].fid_c1 = 0;
}
/* Clear host MAC address */
icssg_class_set_host_mac_addr(prueth->miig_rt, mac);
}
EXPORT_SYMBOL_GPL(icssg_init_emac_mode);
void icssg_init_fw_offload_mode(struct prueth *prueth)
{
u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET;
int i;
/* Set VLAN TABLE address base */
regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
addr << SMEM_VLAN_OFFSET);
regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, FDB_HASH_SIZE_MASK,
FDB_HASH_SIZE << FDB_HASH_SIZE_SHIFT);
/* Set enable VLAN aware mode, and FDBs for all PRUs */
regmap_write(prueth->miig_rt, FDB_GEN_CFG2, FDB_EN_ALL);
prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va +
EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET);
for (i = 0; i < SZ_4K - 1; i++) {
prueth->vlan_tbl[i].fid = i;
prueth->vlan_tbl[i].fid_c1 = 0;
}
if (prueth->hw_bridge_dev)
icssg_class_set_host_mac_addr(prueth->miig_rt, prueth->hw_bridge_dev->dev_addr);
icssg_set_pvid(prueth, prueth->default_vlan, PRUETH_PORT_HOST);
}
EXPORT_SYMBOL_GPL(icssg_init_fw_offload_mode);
int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
{
void __iomem *config = emac->dram.va + ICSSG_CONFIG_OFFSET;
struct icssg_flow_cfg __iomem *flow_cfg;
int ret;
memset_io(config, 0, TAS_GATE_MASK_LIST0);
icssg_miig_queues_init(prueth, slice);
emac->speed = SPEED_1000;
emac->duplex = DUPLEX_FULL;
if (!phy_interface_mode_is_rgmii(emac->phy_if)) {
emac->speed = SPEED_100;
emac->duplex = DUPLEX_FULL;
}
regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET,
ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT);
icssg_miig_set_interface_mode(prueth->miig_rt, slice, emac->phy_if);
if (prueth->is_switch_mode || prueth->is_hsr_offload_mode)
icssg_config_mii_init_fw_offload(emac);
else
icssg_config_mii_init(emac);
icssg_config_ipg(emac);
icssg_update_rgmii_cfg(prueth->miig_rt, emac);
/* set GPI mode */
pruss_cfg_gpimode(prueth->pruss, prueth->pru_id[slice],
PRUSS_GPI_MODE_MII);
/* enable XFR shift for PRU and RTU */
pruss_cfg_xfr_enable(prueth->pruss, PRU_TYPE_PRU, true);
pruss_cfg_xfr_enable(prueth->pruss, PRU_TYPE_RTU, true);
/* set C28 to 0x100 */
pru_rproc_set_ctable(prueth->pru[slice], PRU_C28, 0x100 << 8);
pru_rproc_set_ctable(prueth->rtu[slice], PRU_C28, 0x100 << 8);
pru_rproc_set_ctable(prueth->txpru[slice], PRU_C28, 0x100 << 8);
flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET;
writew(emac->rx_flow_id_base, &flow_cfg->rx_base_flow);
writew(0, &flow_cfg->mgm_base_flow);
writeb(0, config + SPL_PKT_DEFAULT_PRIORITY);
writeb(0, config + QUEUE_NUM_UNTAGGED);
if (prueth->is_switch_mode || prueth->is_hsr_offload_mode)
ret = prueth_fw_offload_buffer_setup(emac);
else
ret = prueth_emac_buffer_setup(emac);
if (ret)
return ret;
emac_r30_cmd_init(emac);
return 0;
}
EXPORT_SYMBOL_GPL(icssg_config);
/* Bitmask for ICSSG r30 commands */
static const struct icssg_r30_cmd emac_r32_bitmask[] = {
{{0xffff0004, 0xffff0100, 0xffff0004, EMAC_NONE}}, /* EMAC_PORT_DISABLE */
{{0xfffb0040, 0xfeff0200, 0xfeff0200, EMAC_NONE}}, /* EMAC_PORT_BLOCK */
{{0xffbb0000, 0xfcff0000, 0xdcfb0000, EMAC_NONE}}, /* EMAC_PORT_FORWARD */
{{0xffbb0000, 0xfcff0000, 0xfcff2000, EMAC_NONE}}, /* EMAC_PORT_FORWARD_WO_LEARNING */
{{0xffff0001, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT ALL */
{{0xfffe0002, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT TAGGED */
{{0xfffc0000, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* ACCEPT UNTAGGED and PRIO */
{{EMAC_NONE, 0xffff0020, EMAC_NONE, EMAC_NONE}}, /* TAS Trigger List change */
{{EMAC_NONE, 0xdfff1000, EMAC_NONE, EMAC_NONE}}, /* TAS set state ENABLE*/
{{EMAC_NONE, 0xefff2000, EMAC_NONE, EMAC_NONE}}, /* TAS set state RESET*/
{{EMAC_NONE, 0xcfff0000, EMAC_NONE, EMAC_NONE}}, /* TAS set state DISABLE*/
{{EMAC_NONE, EMAC_NONE, 0xffff0400, EMAC_NONE}}, /* UC flooding ENABLE*/
{{EMAC_NONE, EMAC_NONE, 0xfbff0000, EMAC_NONE}}, /* UC flooding DISABLE*/
{{EMAC_NONE, EMAC_NONE, 0xffff0800, EMAC_NONE}}, /* MC flooding ENABLE*/
{{EMAC_NONE, EMAC_NONE, 0xf7ff0000, EMAC_NONE}}, /* MC flooding DISABLE*/
{{EMAC_NONE, 0xffff4000, EMAC_NONE, EMAC_NONE}}, /* Preemption on Tx ENABLE*/
{{EMAC_NONE, 0xbfff0000, EMAC_NONE, EMAC_NONE}}, /* Preemption on Tx DISABLE*/
{{0xffff0010, EMAC_NONE, 0xffff0010, EMAC_NONE}}, /* VLAN AWARE*/
{{0xffef0000, EMAC_NONE, 0xffef0000, EMAC_NONE}}, /* VLAN UNWARE*/
{{0xffff2000, EMAC_NONE, EMAC_NONE, EMAC_NONE}}, /* HSR_RX_OFFLOAD_ENABLE */
{{0xdfff0000, EMAC_NONE, EMAC_NONE, EMAC_NONE}} /* HSR_RX_OFFLOAD_DISABLE */
};
int icssg_set_port_state(struct prueth_emac *emac,
enum icssg_port_state_cmd cmd)
{
struct icssg_r30_cmd __iomem *p;
int ret = -ETIMEDOUT;
int done = 0;
int i;
p = emac->dram.va + MGR_R30_CMD_OFFSET;
if (cmd >= ICSSG_EMAC_PORT_MAX_COMMANDS) {
netdev_err(emac->ndev, "invalid port command\n");
return -EINVAL;
}
/* only one command at a time allowed to firmware */
mutex_lock(&emac->cmd_lock);
for (i = 0; i < 4; i++)
writel(emac_r32_bitmask[cmd].cmd[i], &p->cmd[i]);
/* wait for done */
ret = read_poll_timeout(emac_r30_is_done, done, done == 1,
1000, 10000, false, emac);
if (ret == -ETIMEDOUT)
netdev_err(emac->ndev, "timeout waiting for command done\n");
mutex_unlock(&emac->cmd_lock);
return ret;
}
EXPORT_SYMBOL_GPL(icssg_set_port_state);
void icssg_config_half_duplex(struct prueth_emac *emac)
{
u32 val;
if (!emac->half_duplex)
return;
val = get_random_u32();
writel(val, emac->dram.va + HD_RAND_SEED_OFFSET);
}
EXPORT_SYMBOL_GPL(icssg_config_half_duplex);
void icssg_config_set_speed(struct prueth_emac *emac)
{
u8 fw_speed;
switch (emac->speed) {
case SPEED_1000:
fw_speed = FW_LINK_SPEED_1G;
break;
case SPEED_100:
fw_speed = FW_LINK_SPEED_100M;
break;
case SPEED_10:
fw_speed = FW_LINK_SPEED_10M;
break;
default:
/* Other links speeds not supported */
netdev_err(emac->ndev, "Unsupported link speed\n");
return;
}
if (emac->duplex == DUPLEX_HALF)
fw_speed |= FW_LINK_SPEED_HD;
writeb(fw_speed, emac->dram.va + PORT_LINK_SPEED_OFFSET);
}
EXPORT_SYMBOL_GPL(icssg_config_set_speed);
int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
struct mgmt_cmd_rsp *rsp)
{
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
int addr, ret;
addr = icssg_queue_pop(prueth, slice == 0 ?
ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
if (addr < 0)
return addr;
/* First 4 bytes have FW owned buffer linking info which should
* not be touched
*/
memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd));
icssg_queue_push(prueth, slice == 0 ?
ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
ret = read_poll_timeout(icssg_queue_pop, addr, addr >= 0,
2000, 20000000, false, prueth, slice == 0 ?
ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
if (ret) {
netdev_err(emac->ndev, "Timedout sending HWQ message\n");
return ret;
}
memcpy_fromio(rsp, prueth->shram.va + addr, sizeof(*rsp));
/* Return buffer back for to pool */
icssg_queue_push(prueth, slice == 0 ?
ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr);
return 0;
}
EXPORT_SYMBOL_GPL(icssg_send_fdb_msg);
static void icssg_fdb_setup(struct prueth_emac *emac, struct mgmt_cmd *fdb_cmd,
const unsigned char *addr, u8 fid, int cmd)
{
int slice = prueth_emac_slice(emac);
u8 mac_fid[ETH_ALEN + 2];
u16 fdb_slot;
ether_addr_copy(mac_fid, addr);
/* 1-1 VID-FID mapping is already setup */
mac_fid[ETH_ALEN] = fid;
mac_fid[ETH_ALEN + 1] = 0;
fdb_slot = bitrev32(crc32_le(0, mac_fid, 8)) & PRUETH_SWITCH_FDB_MASK;
fdb_cmd->header = ICSSG_FW_MGMT_CMD_HEADER;
fdb_cmd->type = ICSSG_FW_MGMT_FDB_CMD_TYPE;
fdb_cmd->seqnum = ++(emac->prueth->icssg_hwcmdseq);
fdb_cmd->param = cmd;
fdb_cmd->param |= (slice << 4);
memcpy(&fdb_cmd->cmd_args[0], addr, 4);
memcpy(&fdb_cmd->cmd_args[1], &addr[4], 2);
fdb_cmd->cmd_args[2] = fdb_slot;
netdev_dbg(emac->ndev, "MAC %pM slot %X FID %X\n", addr, fdb_slot, fid);
}
int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr,
u8 vid, u8 fid_c2, bool add)
{
struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
struct mgmt_cmd fdb_cmd = { 0 };
u8 fid = vid;
int ret;
icssg_fdb_setup(emac, &fdb_cmd, addr, fid, add ? ICSS_CMD_ADD_FDB : ICSS_CMD_DEL_FDB);
fid_c2 |= ICSSG_FDB_ENTRY_VALID;
fdb_cmd.cmd_args[1] |= ((fid << 16) | (fid_c2 << 24));
ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp);
if (ret)
return ret;
WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum);
if (fdb_cmd_rsp.status == 1)
return 0;
return -EINVAL;
}
EXPORT_SYMBOL_GPL(icssg_fdb_add_del);
int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr,
u8 vid)
{
struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
struct mgmt_cmd fdb_cmd = { 0 };
struct prueth_fdb_slot *slot;
u8 fid = vid;
int ret, i;
icssg_fdb_setup(emac, &fdb_cmd, addr, fid, ICSS_CMD_GET_FDB_SLOT);
fdb_cmd.cmd_args[1] |= fid << 16;
ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp);
if (ret)
return ret;
WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum);
slot = (struct prueth_fdb_slot __force *)(emac->dram.va + FDB_CMD_BUFFER);
for (i = 0; i < 4; i++) {
if (ether_addr_equal(addr, slot->mac) && vid == slot->fid)
return (slot->fid_c2 & ~ICSSG_FDB_ENTRY_VALID);
slot++;
}
return 0;
}
EXPORT_SYMBOL_GPL(icssg_fdb_lookup);
void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask,
u8 untag_mask, bool add)
{
struct prueth *prueth = emac->prueth;
struct prueth_vlan_tbl *tbl;
u8 fid_c1;
tbl = prueth->vlan_tbl;
spin_lock(&prueth->vtbl_lock);
fid_c1 = tbl[vid].fid_c1;
/* FID_C1: bit0..2 port membership mask,
* bit3..5 tagging mask for each port
* bit6 Stream VID (not handled currently)
* bit7 MC flood (not handled currently)
*/
if (add) {
fid_c1 |= (port_mask | port_mask << 3);
fid_c1 &= ~(untag_mask << 3);
} else {
fid_c1 &= ~(port_mask | port_mask << 3);
}
tbl[vid].fid_c1 = fid_c1;
spin_unlock(&prueth->vtbl_lock);
}
EXPORT_SYMBOL_GPL(icssg_vtbl_modify);
u16 icssg_get_pvid(struct prueth_emac *emac)
{
struct prueth *prueth = emac->prueth;
u32 pvid;
if (emac->port_id == PRUETH_PORT_MII0)
pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET);
else
pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET);
pvid = pvid >> 24;
return pvid;
}
EXPORT_SYMBOL_GPL(icssg_get_pvid);
void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port)
{
u32 pvid;
/* only 256 VLANs are supported */
pvid = (u32 __force)cpu_to_be32((ETH_P_8021Q << 16) | (vid & 0xff));
if (port == PRUETH_PORT_MII0)
writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET);
else if (port == PRUETH_PORT_MII1)
writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET);
else
writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET);
}
EXPORT_SYMBOL_GPL(icssg_set_pvid);
int emac_fdb_flow_id_updated(struct prueth_emac *emac)
{
struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
int slice = prueth_emac_slice(emac);
struct mgmt_cmd fdb_cmd = { 0 };
int ret;
fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER;
fdb_cmd.type = ICSSG_FW_MGMT_FDB_CMD_TYPE_RX_FLOW;
fdb_cmd.seqnum = ++(emac->prueth->icssg_hwcmdseq);
fdb_cmd.param = 0;
fdb_cmd.param |= (slice << 4);
fdb_cmd.cmd_args[0] = 0;
ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp);
if (ret)
return ret;
WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum);
return fdb_cmd_rsp.status == 1 ? 0 : -EINVAL;
}
EXPORT_SYMBOL_GPL(emac_fdb_flow_id_updated);
@@ -0,0 +1,335 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Texas Instruments ICSSG Ethernet driver
*
* Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#ifndef __NET_TI_ICSSG_CONFIG_H
#define __NET_TI_ICSSG_CONFIG_H
struct icssg_buffer_pool_cfg {
__le32 addr;
__le32 len;
} __packed;
struct icssg_flow_cfg {
__le16 rx_base_flow;
__le16 mgm_base_flow;
} __packed;
#define PRUETH_PKT_TYPE_CMD 0x10
#define PRUETH_NAV_PS_DATA_SIZE 16 /* Protocol specific data size */
#define PRUETH_NAV_SW_DATA_SIZE 16 /* SW related data size */
#define PRUETH_MAX_TX_DESC 512
#define PRUETH_MAX_RX_DESC 512
#define PRUETH_MAX_RX_FLOWS 1 /* excluding default flow */
#define PRUETH_RX_FLOW_DATA 0
/* Defines for forwarding path buffer pools:
* - used by firmware to store packets to be forwarded to other port
* - 8 total pools per slice
* - only used in switch mode (as no forwarding in mac mode)
*/
#define PRUETH_NUM_FWD_BUF_POOLS_PER_SLICE 8
#define PRUETH_SW_FWD_BUF_POOL_SIZE (SZ_8K)
/* Defines for local injection path buffer pools:
* - used by firmware to store packets received from host core
* - 16 total pools per slice
* - 8 pools per port per slice and each slice handles both ports
* - only 4 out of 8 pools used per port (as only 4 real QoS levels in ICSSG)
* - switch mode: 8 total pools used
* - mac mode: 4 total pools used
*/
#define PRUETH_NUM_LI_BUF_POOLS_PER_SLICE 16
#define PRUETH_NUM_LI_BUF_POOLS_PER_PORT_PER_SLICE 8
#define PRUETH_SW_LI_BUF_POOL_SIZE SZ_4K
#define PRUETH_SW_USED_LI_BUF_POOLS_PER_SLICE 8
#define PRUETH_SW_USED_LI_BUF_POOLS_PER_PORT_PER_SLICE 4
#define PRUETH_EMAC_LI_BUF_POOL_SIZE SZ_8K
#define PRUETH_EMAC_USED_LI_BUF_POOLS_PER_SLICE 4
#define PRUETH_EMAC_USED_LI_BUF_POOLS_PER_PORT_PER_SLICE 4
/* Defines for host egress path - express and preemptible buffers
* - used by firmware to store express and preemptible packets
* to be transmitted to host core
* - used by both mac/switch modes
*/
#define PRUETH_SW_HOST_EXP_BUF_POOL_SIZE SZ_16K
#define PRUETH_SW_HOST_PRE_BUF_POOL_SIZE (SZ_16K - SZ_2K)
#define PRUETH_EMAC_HOST_EXP_BUF_POOL_SIZE PRUETH_SW_HOST_EXP_BUF_POOL_SIZE
#define PRUETH_EMAC_HOST_PRE_BUF_POOL_SIZE PRUETH_SW_HOST_PRE_BUF_POOL_SIZE
/* Buffer used by firmware to temporarily store packet to be dropped */
#define PRUETH_SW_DROP_PKT_BUF_SIZE SZ_2K
#define PRUETH_EMAC_DROP_PKT_BUF_SIZE PRUETH_SW_DROP_PKT_BUF_SIZE
/* Total switch mode memory usage for buffers per slice */
#define PRUETH_SW_TOTAL_BUF_SIZE_PER_SLICE \
(PRUETH_SW_FWD_BUF_POOL_SIZE * PRUETH_NUM_FWD_BUF_POOLS_PER_SLICE + \
PRUETH_SW_LI_BUF_POOL_SIZE * PRUETH_SW_USED_LI_BUF_POOLS_PER_SLICE + \
PRUETH_SW_HOST_EXP_BUF_POOL_SIZE + \
PRUETH_SW_HOST_PRE_BUF_POOL_SIZE + \
PRUETH_SW_DROP_PKT_BUF_SIZE)
/* Total switch mode memory usage for all buffers */
#define PRUETH_SW_TOTAL_BUF_SIZE \
(2 * PRUETH_SW_TOTAL_BUF_SIZE_PER_SLICE)
/* Total mac mode memory usage for buffers per slice */
#define PRUETH_EMAC_TOTAL_BUF_SIZE_PER_SLICE \
(PRUETH_EMAC_LI_BUF_POOL_SIZE * \
PRUETH_EMAC_USED_LI_BUF_POOLS_PER_SLICE + \
PRUETH_EMAC_HOST_EXP_BUF_POOL_SIZE + \
PRUETH_EMAC_HOST_PRE_BUF_POOL_SIZE + \
PRUETH_EMAC_DROP_PKT_BUF_SIZE)
/* Total mac mode memory usage for all buffers */
#define PRUETH_EMAC_TOTAL_BUF_SIZE \
(2 * PRUETH_EMAC_TOTAL_BUF_SIZE_PER_SLICE)
/* Size of 1 bank of MSMC/OC_SRAM memory */
#define MSMC_RAM_BANK_SIZE SZ_256K
#define PRUETH_SWITCH_FDB_MASK ((SIZE_OF_FDB / NUMBER_OF_FDB_BUCKET_ENTRIES) - 1)
struct icssg_rxq_ctx {
__le32 start[3];
__le32 end;
} __packed;
/* Load time Fiwmware Configuration */
#define ICSSG_FW_MGMT_CMD_HEADER 0x81
#define ICSSG_FW_MGMT_FDB_CMD_TYPE 0x03
#define ICSSG_FW_MGMT_CMD_TYPE 0x04
#define ICSSG_FW_MGMT_PKT 0x80000000
#define ICSSG_FW_MGMT_FDB_CMD_TYPE_RX_FLOW 0x05
struct icssg_r30_cmd {
u32 cmd[4];
} __packed;
enum icssg_port_state_cmd {
ICSSG_EMAC_PORT_DISABLE = 0,
ICSSG_EMAC_PORT_BLOCK,
ICSSG_EMAC_PORT_FORWARD,
ICSSG_EMAC_PORT_FORWARD_WO_LEARNING,
ICSSG_EMAC_PORT_ACCEPT_ALL,
ICSSG_EMAC_PORT_ACCEPT_TAGGED,
ICSSG_EMAC_PORT_ACCEPT_UNTAGGED_N_PRIO,
ICSSG_EMAC_PORT_TAS_TRIGGER,
ICSSG_EMAC_PORT_TAS_ENABLE,
ICSSG_EMAC_PORT_TAS_RESET,
ICSSG_EMAC_PORT_TAS_DISABLE,
ICSSG_EMAC_PORT_UC_FLOODING_ENABLE,
ICSSG_EMAC_PORT_UC_FLOODING_DISABLE,
ICSSG_EMAC_PORT_MC_FLOODING_ENABLE,
ICSSG_EMAC_PORT_MC_FLOODING_DISABLE,
ICSSG_EMAC_PORT_PREMPT_TX_ENABLE,
ICSSG_EMAC_PORT_PREMPT_TX_DISABLE,
ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE,
ICSSG_EMAC_PORT_VLAN_AWARE_DISABLE,
ICSSG_EMAC_HSR_RX_OFFLOAD_ENABLE,
ICSSG_EMAC_HSR_RX_OFFLOAD_DISABLE,
ICSSG_EMAC_PORT_MAX_COMMANDS
};
#define EMAC_NONE 0xffff0000
#define EMAC_PRU0_P_DI 0xffff0004
#define EMAC_PRU1_P_DI 0xffff0040
#define EMAC_TX_P_DI 0xffff0100
#define EMAC_PRU0_P_EN 0xfffb0000
#define EMAC_PRU1_P_EN 0xffbf0000
#define EMAC_TX_P_EN 0xfeff0000
#define EMAC_P_BLOCK 0xffff0040
#define EMAC_TX_P_BLOCK 0xffff0200
#define EMAC_P_UNBLOCK 0xffbf0000
#define EMAC_TX_P_UNBLOCK 0xfdff0000
#define EMAC_LEAN_EN 0xfff70000
#define EMAC_LEAN_DI 0xffff0008
#define EMAC_ACCEPT_ALL 0xffff0001
#define EMAC_ACCEPT_TAG 0xfffe0002
#define EMAC_ACCEPT_PRIOR 0xfffc0000
/* Config area lies in DRAM */
#define ICSSG_CONFIG_OFFSET 0x0
/* Config area lies in shared RAM */
#define ICSSG_CONFIG_OFFSET_SLICE0 0
#define ICSSG_CONFIG_OFFSET_SLICE1 0x8000
#define ICSSG_NUM_NORMAL_PDS 64
#define ICSSG_NUM_SPECIAL_PDS 16
#define ICSSG_NORMAL_PD_SIZE 8
#define ICSSG_SPECIAL_PD_SIZE 20
#define ICSSG_FLAG_MASK 0xff00ffff
/* SR1.0-specific bits */
#define PRUETH_MAX_RX_FLOWS_SR1 4 /* excluding default flow */
#define PRUETH_RX_FLOW_DATA_SR1 3 /* highest priority flow */
#define PRUETH_MAX_RX_MGM_DESC_SR1 8
#define PRUETH_MAX_RX_MGM_FLOWS_SR1 2 /* excluding default flow */
#define PRUETH_RX_MGM_FLOW_RESPONSE_SR1 0
#define PRUETH_RX_MGM_FLOW_TIMESTAMP_SR1 1
#define PRUETH_NUM_BUF_POOLS_SR1 16
#define PRUETH_EMAC_BUF_POOL_START_SR1 8
#define PRUETH_EMAC_BUF_POOL_MIN_SIZE_SR1 128
#define PRUETH_EMAC_BUF_SIZE_SR1 1536
#define PRUETH_EMAC_NUM_BUF_SR1 4
#define PRUETH_EMAC_BUF_POOL_SIZE_SR1 (PRUETH_EMAC_NUM_BUF_SR1 * \
PRUETH_EMAC_BUF_SIZE_SR1)
#define MSMC_RAM_SIZE_SR1 (SZ_64K + SZ_32K + SZ_2K) /* 0x1880 x 8 x 2 */
struct icssg_sr1_config {
__le32 status; /* Firmware status */
__le32 addr_lo; /* MSMC Buffer pool base address low. */
__le32 addr_hi; /* MSMC Buffer pool base address high. Must be 0 */
__le32 tx_buf_sz[16]; /* Array of buffer pool sizes */
__le32 num_tx_threads; /* Number of active egress threads, 1 to 4 */
__le32 tx_rate_lim_en; /* Bitmask: Egress rate limit en per thread */
__le32 rx_flow_id; /* RX flow id for first rx ring */
__le32 rx_mgr_flow_id; /* RX flow id for the first management ring */
__le32 flags; /* TBD */
__le32 n_burst; /* for debug */
__le32 rtu_status; /* RTU status */
__le32 info; /* reserved */
__le32 reserve;
__le32 rand_seed; /* Used for the random number generation at fw */
} __packed;
/* SR1.0 shutdown command to stop processing at firmware.
* Command format: 0x8101ss00, where
* - ss: sequence number. Currently not used by driver.
*/
#define ICSSG_SHUTDOWN_CMD_SR1 0x81010000
/* SR1.0 pstate speed/duplex command to set speed and duplex settings
* in firmware.
* Command format: 0x8102ssPN, where
* - ss: sequence number. Currently not used by driver.
* - P: port number (for switch mode).
* - N: Speed/Duplex state:
* 0x0 - 10Mbps/Half duplex;
* 0x8 - 10Mbps/Full duplex;
* 0x2 - 100Mbps/Half duplex;
* 0xa - 100Mbps/Full duplex;
* 0xc - 1Gbps/Full duplex;
* NOTE: The above are the same value as bits [3..1](slice 0)
* or bits [7..5](slice 1) of RGMII CFG register.
*/
#define ICSSG_PSTATE_SPEED_DUPLEX_CMD_SR1 0x81020000
struct icssg_setclock_desc {
u8 request;
u8 restore;
u8 acknowledgment;
u8 cmp_status;
u32 margin;
u32 cyclecounter0_set;
u32 cyclecounter1_set;
u32 iepcount_set;
u32 rsvd1;
u32 rsvd2;
u32 CMP0_current;
u32 iepcount_current;
u32 difference;
u32 cyclecounter0_new;
u32 cyclecounter1_new;
u32 CMP0_new;
} __packed;
#define ICSSG_CMD_POP_SLICE0 56
#define ICSSG_CMD_POP_SLICE1 60
#define ICSSG_CMD_PUSH_SLICE0 57
#define ICSSG_CMD_PUSH_SLICE1 61
#define ICSSG_RSP_POP_SLICE0 58
#define ICSSG_RSP_POP_SLICE1 62
#define ICSSG_RSP_PUSH_SLICE0 56
#define ICSSG_RSP_PUSH_SLICE1 60
#define ICSSG_TS_POP_SLICE0 59
#define ICSSG_TS_POP_SLICE1 63
#define ICSSG_TS_PUSH_SLICE0 40
#define ICSSG_TS_PUSH_SLICE1 41
struct mgmt_cmd {
u8 param;
u8 seqnum;
u8 type;
u8 header;
u32 cmd_args[3];
};
struct mgmt_cmd_rsp {
u32 reserved;
u8 status;
u8 seqnum;
u8 type;
u8 header;
u32 cmd_args[3];
};
/* FDB FID_C2 flag definitions */
/* Indicates host port membership.*/
#define ICSSG_FDB_ENTRY_P0_MEMBERSHIP BIT(0)
/* Indicates that MAC ID is connected to physical port 1 */
#define ICSSG_FDB_ENTRY_P1_MEMBERSHIP BIT(1)
/* Indicates that MAC ID is connected to physical port 2 */
#define ICSSG_FDB_ENTRY_P2_MEMBERSHIP BIT(2)
/* Ageable bit is set for learned entries and cleared for static entries */
#define ICSSG_FDB_ENTRY_AGEABLE BIT(3)
/* If set for DA then packet is determined to be a special packet */
#define ICSSG_FDB_ENTRY_BLOCK BIT(4)
/* If set for DA then the SA from the packet is not learned */
#define ICSSG_FDB_ENTRY_SECURE BIT(5)
/* If set, it means packet has been seen recently with source address + FID
* matching MAC address/FID of entry
*/
#define ICSSG_FDB_ENTRY_TOUCHED BIT(6)
/* Set if entry is valid */
#define ICSSG_FDB_ENTRY_VALID BIT(7)
/**
* struct prueth_vlan_tbl - VLAN table entries struct in ICSSG SMEM
* @fid_c1: membership and forwarding rules flag to this table. See
* above to defines for bit definitions
* @fid: FDB index for this VID (there is 1-1 mapping b/w VID and FID)
*/
struct prueth_vlan_tbl {
u8 fid_c1;
u8 fid;
} __packed;
/**
* struct prueth_fdb_slot - Result of FDB slot lookup
* @mac: MAC address
* @fid: fid to be associated with MAC
* @fid_c2: FID_C2 entry for this MAC
*/
struct prueth_fdb_slot {
u8 mac[ETH_ALEN];
u8 fid;
u8 fid_c2;
} __packed;
enum icssg_ietfpe_verify_states {
ICSSG_IETFPE_STATE_UNKNOWN = 0,
ICSSG_IETFPE_STATE_INITIAL,
ICSSG_IETFPE_STATE_VERIFYING,
ICSSG_IETFPE_STATE_SUCCEEDED,
ICSSG_IETFPE_STATE_FAILED,
ICSSG_IETFPE_STATE_DISABLED
};
#endif /* __NET_TI_ICSSG_CONFIG_H */
@@ -0,0 +1,321 @@
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments ICSSG Ethernet driver
*
* Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#include "icssg_prueth.h"
#include "icssg_stats.h"
static void emac_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
strscpy(info->driver, dev_driver_string(prueth->dev),
sizeof(info->driver));
strscpy(info->bus_info, dev_name(prueth->dev), sizeof(info->bus_info));
}
static u32 emac_get_msglevel(struct net_device *ndev)
{
struct prueth_emac *emac = netdev_priv(ndev);
return emac->msg_enable;
}
static void emac_set_msglevel(struct net_device *ndev, u32 value)
{
struct prueth_emac *emac = netdev_priv(ndev);
emac->msg_enable = value;
}
static int emac_get_link_ksettings(struct net_device *ndev,
struct ethtool_link_ksettings *ecmd)
{
return phy_ethtool_get_link_ksettings(ndev, ecmd);
}
static int emac_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *ecmd)
{
return phy_ethtool_set_link_ksettings(ndev, ecmd);
}
static int emac_get_eee(struct net_device *ndev, struct ethtool_keee *edata)
{
if (!ndev->phydev)
return -EOPNOTSUPP;
return phy_ethtool_get_eee(ndev->phydev, edata);
}
static int emac_set_eee(struct net_device *ndev, struct ethtool_keee *edata)
{
if (!ndev->phydev)
return -EOPNOTSUPP;
return phy_ethtool_set_eee(ndev->phydev, edata);
}
static int emac_nway_reset(struct net_device *ndev)
{
return phy_ethtool_nway_reset(ndev);
}
static int emac_get_sset_count(struct net_device *ndev, int stringset)
{
struct prueth_emac *emac = netdev_priv(ndev);
switch (stringset) {
case ETH_SS_STATS:
if (emac->prueth->pa_stats)
return ICSSG_NUM_ETHTOOL_STATS;
else
return ICSSG_NUM_ETHTOOL_STATS - ICSSG_NUM_PA_STATS;
default:
return -EOPNOTSUPP;
}
}
static void emac_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
{
struct prueth_emac *emac = netdev_priv(ndev);
u8 *p = data;
int i;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++)
if (!icssg_all_miig_stats[i].standard_stats)
ethtool_puts(&p, icssg_all_miig_stats[i].name);
if (emac->prueth->pa_stats)
for (i = 0; i < ARRAY_SIZE(icssg_all_pa_stats); i++)
ethtool_puts(&p, icssg_all_pa_stats[i].name);
break;
default:
break;
}
}
static void emac_get_ethtool_stats(struct net_device *ndev,
struct ethtool_stats *stats, u64 *data)
{
struct prueth_emac *emac = netdev_priv(ndev);
int i;
emac_update_hardware_stats(emac);
for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++)
if (!icssg_all_miig_stats[i].standard_stats)
*(data++) = emac->stats[i];
if (emac->prueth->pa_stats)
for (i = 0; i < ARRAY_SIZE(icssg_all_pa_stats); i++)
*(data++) = emac->pa_stats[i];
}
static int emac_get_ts_info(struct net_device *ndev,
struct kernel_ethtool_ts_info *info)
{
struct prueth_emac *emac = netdev_priv(ndev);
info->so_timestamping =
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->phc_index = icss_iep_get_ptp_clock_idx(emac->iep);
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
return 0;
}
static int emac_set_channels(struct net_device *ndev,
struct ethtool_channels *ch)
{
struct prueth_emac *emac = netdev_priv(ndev);
/* Check if interface is up. Can change the num queues when
* the interface is down.
*/
if (netif_running(emac->ndev))
return -EBUSY;
emac->tx_ch_num = ch->tx_count;
if (emac->is_sr1)
emac->tx_ch_num++;
return 0;
}
static void emac_get_channels(struct net_device *ndev,
struct ethtool_channels *ch)
{
struct prueth_emac *emac = netdev_priv(ndev);
ch->max_rx = 1;
ch->max_tx = PRUETH_MAX_TX_QUEUES;
/* Disable multiple TX channels due to timeouts
* when using more than one queue */
if (emac->is_sr1)
ch->max_tx = 1;
ch->rx_count = 1;
ch->tx_count = emac->tx_ch_num;
if (emac->is_sr1)
ch->tx_count--;
}
static const struct ethtool_rmon_hist_range emac_rmon_ranges[] = {
{ 0, 64},
{ 65, 128},
{ 129, 256},
{ 257, 512},
{ 513, PRUETH_MAX_PKT_SIZE},
{}
};
static void emac_get_rmon_stats(struct net_device *ndev,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
struct prueth_emac *emac = netdev_priv(ndev);
*ranges = emac_rmon_ranges;
rmon_stats->undersize_pkts = emac_get_stat_by_name(emac, "rx_bucket1_frames") -
emac_get_stat_by_name(emac, "rx_64B_frames");
rmon_stats->hist[0] = emac_get_stat_by_name(emac, "rx_bucket1_frames");
rmon_stats->hist[1] = emac_get_stat_by_name(emac, "rx_bucket2_frames");
rmon_stats->hist[2] = emac_get_stat_by_name(emac, "rx_bucket3_frames");
rmon_stats->hist[3] = emac_get_stat_by_name(emac, "rx_bucket4_frames");
rmon_stats->hist[4] = emac_get_stat_by_name(emac, "rx_bucket5_frames");
rmon_stats->hist_tx[0] = emac_get_stat_by_name(emac, "tx_bucket1_frames");
rmon_stats->hist_tx[1] = emac_get_stat_by_name(emac, "tx_bucket2_frames");
rmon_stats->hist_tx[2] = emac_get_stat_by_name(emac, "tx_bucket3_frames");
rmon_stats->hist_tx[3] = emac_get_stat_by_name(emac, "tx_bucket4_frames");
rmon_stats->hist_tx[4] = emac_get_stat_by_name(emac, "tx_bucket5_frames");
}
static int emac_get_coalesce(struct net_device *ndev,
struct ethtool_coalesce *coal,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth_tx_chn *tx_chn;
tx_chn = &emac->tx_chns[0];
coal->rx_coalesce_usecs = emac->rx_pace_timeout_ns / 1000;
coal->tx_coalesce_usecs = tx_chn->tx_pace_timeout_ns / 1000;
return 0;
}
static int emac_get_per_queue_coalesce(struct net_device *ndev, u32 queue,
struct ethtool_coalesce *coal)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth_tx_chn *tx_chn;
if (queue >= PRUETH_MAX_TX_QUEUES)
return -EINVAL;
tx_chn = &emac->tx_chns[queue];
coal->tx_coalesce_usecs = tx_chn->tx_pace_timeout_ns / 1000;
return 0;
}
static int emac_set_coalesce(struct net_device *ndev,
struct ethtool_coalesce *coal,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
struct prueth_tx_chn *tx_chn;
tx_chn = &emac->tx_chns[0];
if (coal->rx_coalesce_usecs &&
coal->rx_coalesce_usecs < ICSSG_MIN_COALESCE_USECS) {
dev_info(prueth->dev, "defaulting to min value of %dus for rx-usecs\n",
ICSSG_MIN_COALESCE_USECS);
coal->rx_coalesce_usecs = ICSSG_MIN_COALESCE_USECS;
}
if (coal->tx_coalesce_usecs &&
coal->tx_coalesce_usecs < ICSSG_MIN_COALESCE_USECS) {
dev_info(prueth->dev, "defaulting to min value of %dus for tx-usecs\n",
ICSSG_MIN_COALESCE_USECS);
coal->tx_coalesce_usecs = ICSSG_MIN_COALESCE_USECS;
}
emac->rx_pace_timeout_ns = coal->rx_coalesce_usecs * 1000;
tx_chn->tx_pace_timeout_ns = coal->tx_coalesce_usecs * 1000;
return 0;
}
static int emac_set_per_queue_coalesce(struct net_device *ndev, u32 queue,
struct ethtool_coalesce *coal)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
struct prueth_tx_chn *tx_chn;
if (queue >= PRUETH_MAX_TX_QUEUES)
return -EINVAL;
tx_chn = &emac->tx_chns[queue];
if (coal->tx_coalesce_usecs &&
coal->tx_coalesce_usecs < ICSSG_MIN_COALESCE_USECS) {
dev_info(prueth->dev, "defaulting to min value of %dus for tx-usecs for tx-%u\n",
ICSSG_MIN_COALESCE_USECS, queue);
coal->tx_coalesce_usecs = ICSSG_MIN_COALESCE_USECS;
}
tx_chn->tx_pace_timeout_ns = coal->tx_coalesce_usecs * 1000;
return 0;
}
const struct ethtool_ops icssg_ethtool_ops = {
.get_drvinfo = emac_get_drvinfo,
.get_msglevel = emac_get_msglevel,
.set_msglevel = emac_set_msglevel,
.get_sset_count = emac_get_sset_count,
.get_ethtool_stats = emac_get_ethtool_stats,
.get_strings = emac_get_strings,
.get_ts_info = emac_get_ts_info,
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
ETHTOOL_COALESCE_TX_USECS,
.get_coalesce = emac_get_coalesce,
.set_coalesce = emac_set_coalesce,
.get_per_queue_coalesce = emac_get_per_queue_coalesce,
.set_per_queue_coalesce = emac_set_per_queue_coalesce,
.get_channels = emac_get_channels,
.set_channels = emac_set_channels,
.get_link_ksettings = emac_get_link_ksettings,
.set_link_ksettings = emac_set_link_ksettings,
.get_link = ethtool_op_get_link,
.get_eee = emac_get_eee,
.set_eee = emac_set_eee,
.nway_reset = emac_nway_reset,
.get_rmon_stats = emac_get_rmon_stats,
};
EXPORT_SYMBOL_GPL(icssg_ethtool_ops);
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments ICSSG Ethernet Driver
*
* Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#include <linux/etherdevice.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include "icssg_mii_rt.h"
#include "icssg_prueth.h"
void icssg_mii_update_ipg(struct regmap *mii_rt, int mii, u32 ipg)
{
u32 val;
if (mii == ICSS_MII0) {
regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG0, ipg);
} else {
regmap_read(mii_rt, PRUSS_MII_RT_TX_IPG0, &val);
regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG1, ipg);
regmap_write(mii_rt, PRUSS_MII_RT_TX_IPG0, val);
}
}
void icssg_mii_update_mtu(struct regmap *mii_rt, int mii, int mtu)
{
mtu += (ETH_HLEN + ETH_FCS_LEN);
if (mii == ICSS_MII0) {
regmap_update_bits(mii_rt,
PRUSS_MII_RT_RX_FRMS0,
PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK,
(mtu - 1) << PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT);
} else {
regmap_update_bits(mii_rt,
PRUSS_MII_RT_RX_FRMS1,
PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK,
(mtu - 1) << PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT);
}
}
EXPORT_SYMBOL_GPL(icssg_mii_update_mtu);
void icssg_update_rgmii_cfg(struct regmap *miig_rt, struct prueth_emac *emac)
{
u32 gig_en_mask, gig_val = 0, full_duplex_mask, full_duplex_val = 0;
int slice = prueth_emac_slice(emac);
u32 inband_en_mask, inband_val = 0;
gig_en_mask = (slice == ICSS_MII0) ? RGMII_CFG_GIG_EN_MII0 :
RGMII_CFG_GIG_EN_MII1;
if (emac->speed == SPEED_1000)
gig_val = gig_en_mask;
regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, gig_en_mask, gig_val);
inband_en_mask = (slice == ICSS_MII0) ? RGMII_CFG_INBAND_EN_MII0 :
RGMII_CFG_INBAND_EN_MII1;
if (emac->speed == SPEED_10 && phy_interface_mode_is_rgmii(emac->phy_if))
inband_val = inband_en_mask;
regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, inband_en_mask, inband_val);
full_duplex_mask = (slice == ICSS_MII0) ? RGMII_CFG_FULL_DUPLEX_MII0 :
RGMII_CFG_FULL_DUPLEX_MII1;
if (emac->duplex == DUPLEX_FULL)
full_duplex_val = full_duplex_mask;
regmap_update_bits(miig_rt, RGMII_CFG_OFFSET, full_duplex_mask,
full_duplex_val);
}
EXPORT_SYMBOL_GPL(icssg_update_rgmii_cfg);
void icssg_miig_set_interface_mode(struct regmap *miig_rt, int mii, phy_interface_t phy_if)
{
u32 val, mask, shift;
mask = mii == ICSS_MII0 ? ICSSG_CFG_MII0_MODE : ICSSG_CFG_MII1_MODE;
shift = mii == ICSS_MII0 ? ICSSG_CFG_MII0_MODE_SHIFT : ICSSG_CFG_MII1_MODE_SHIFT;
val = MII_MODE_RGMII;
if (phy_if == PHY_INTERFACE_MODE_MII)
val = MII_MODE_MII;
val <<= shift;
regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, mask, val);
regmap_read(miig_rt, ICSSG_CFG_OFFSET, &val);
}
u32 icssg_rgmii_cfg_get_bitfield(struct regmap *miig_rt, u32 mask, u32 shift)
{
u32 val;
regmap_read(miig_rt, RGMII_CFG_OFFSET, &val);
val &= mask;
val >>= shift;
return val;
}
u32 icssg_rgmii_get_speed(struct regmap *miig_rt, int mii)
{
u32 shift = RGMII_CFG_SPEED_MII0_SHIFT, mask = RGMII_CFG_SPEED_MII0;
if (mii == ICSS_MII1) {
shift = RGMII_CFG_SPEED_MII1_SHIFT;
mask = RGMII_CFG_SPEED_MII1;
}
return icssg_rgmii_cfg_get_bitfield(miig_rt, mask, shift);
}
EXPORT_SYMBOL_GPL(icssg_rgmii_get_speed);
u32 icssg_rgmii_get_fullduplex(struct regmap *miig_rt, int mii)
{
u32 shift = RGMII_CFG_FULLDUPLEX_MII0_SHIFT;
u32 mask = RGMII_CFG_FULLDUPLEX_MII0;
if (mii == ICSS_MII1) {
shift = RGMII_CFG_FULLDUPLEX_MII1_SHIFT;
mask = RGMII_CFG_FULLDUPLEX_MII1;
}
return icssg_rgmii_cfg_get_bitfield(miig_rt, mask, shift);
}
EXPORT_SYMBOL_GPL(icssg_rgmii_get_fullduplex);
@@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* PRU-ICSS MII_RT register definitions
*
* Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com
*/
#ifndef __NET_PRUSS_MII_RT_H__
#define __NET_PRUSS_MII_RT_H__
#include <linux/if_ether.h>
#include <linux/phy.h>
/* PRUSS_MII_RT Registers */
#define PRUSS_MII_RT_RXCFG0 0x0
#define PRUSS_MII_RT_RXCFG1 0x4
#define PRUSS_MII_RT_TXCFG0 0x10
#define PRUSS_MII_RT_TXCFG1 0x14
#define PRUSS_MII_RT_TX_CRC0 0x20
#define PRUSS_MII_RT_TX_CRC1 0x24
#define PRUSS_MII_RT_TX_IPG0 0x30
#define PRUSS_MII_RT_TX_IPG1 0x34
#define PRUSS_MII_RT_PRS0 0x38
#define PRUSS_MII_RT_PRS1 0x3c
#define PRUSS_MII_RT_RX_FRMS0 0x40
#define PRUSS_MII_RT_RX_FRMS1 0x44
#define PRUSS_MII_RT_RX_PCNT0 0x48
#define PRUSS_MII_RT_RX_PCNT1 0x4c
#define PRUSS_MII_RT_RX_ERR0 0x50
#define PRUSS_MII_RT_RX_ERR1 0x54
/* PRUSS_MII_RT_RXCFG0/1 bits */
#define PRUSS_MII_RT_RXCFG_RX_ENABLE BIT(0)
#define PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS BIT(1)
#define PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE BIT(2)
#define PRUSS_MII_RT_RXCFG_RX_MUX_SEL BIT(3)
#define PRUSS_MII_RT_RXCFG_RX_L2_EN BIT(4)
#define PRUSS_MII_RT_RXCFG_RX_BYTE_SWAP BIT(5)
#define PRUSS_MII_RT_RXCFG_RX_AUTO_FWD_PRE BIT(6)
#define PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS BIT(9)
/* PRUSS_MII_RT_TXCFG0/1 bits */
#define PRUSS_MII_RT_TXCFG_TX_ENABLE BIT(0)
#define PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE BIT(1)
#define PRUSS_MII_RT_TXCFG_TX_EN_MODE BIT(2)
#define PRUSS_MII_RT_TXCFG_TX_BYTE_SWAP BIT(3)
#define PRUSS_MII_RT_TXCFG_TX_MUX_SEL BIT(8)
#define PRUSS_MII_RT_TXCFG_PRE_TX_AUTO_SEQUENCE BIT(9)
#define PRUSS_MII_RT_TXCFG_PRE_TX_AUTO_ESC_ERR BIT(10)
#define PRUSS_MII_RT_TXCFG_TX_32_MODE_EN BIT(11)
#define PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN BIT(12) /* SR2.0 onwards */
#define PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT 16
#define PRUSS_MII_RT_TXCFG_TX_START_DELAY_MASK GENMASK(25, 16)
#define PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT 28
#define PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK GENMASK(30, 28)
/* PRUSS_MII_RT_TX_IPG0/1 bits */
#define PRUSS_MII_RT_TX_IPG_IPG_SHIFT 0
#define PRUSS_MII_RT_TX_IPG_IPG_MASK GENMASK(9, 0)
/* PRUSS_MII_RT_PRS0/1 bits */
#define PRUSS_MII_RT_PRS_COL BIT(0)
#define PRUSS_MII_RT_PRS_CRS BIT(1)
/* PRUSS_MII_RT_RX_FRMS0/1 bits */
#define PRUSS_MII_RT_RX_FRMS_MIN_FRM_SHIFT 0
#define PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK GENMASK(15, 0)
#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_SHIFT 16
#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK GENMASK(31, 16)
/* Min/Max in MII_RT_RX_FRMS */
/* For EMAC and Switch */
#define PRUSS_MII_RT_RX_FRMS_MAX (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
#define PRUSS_MII_RT_RX_FRMS_MIN_FRM (64)
/* for HSR and PRP */
#define PRUSS_MII_RT_RX_FRMS_MAX_FRM_LRE (PRUSS_MII_RT_RX_FRMS_MAX + \
ICSS_LRE_TAG_RCT_SIZE)
/* PRUSS_MII_RT_RX_PCNT0/1 bits */
#define PRUSS_MII_RT_RX_PCNT_MIN_PCNT_SHIFT 0
#define PRUSS_MII_RT_RX_PCNT_MIN_PCNT_MASK GENMASK(3, 0)
#define PRUSS_MII_RT_RX_PCNT_MAX_PCNT_SHIFT 4
#define PRUSS_MII_RT_RX_PCNT_MAX_PCNT_MASK GENMASK(7, 4)
/* PRUSS_MII_RT_RX_ERR0/1 bits */
#define PRUSS_MII_RT_RX_ERR_MIN_PCNT_ERR BIT(0)
#define PRUSS_MII_RT_RX_ERR_MAX_PCNT_ERR BIT(1)
#define PRUSS_MII_RT_RX_ERR_MIN_FRM_ERR BIT(2)
#define PRUSS_MII_RT_RX_ERR_MAX_FRM_ERR BIT(3)
#define ICSSG_CFG_OFFSET 0
#define RGMII_CFG_OFFSET 4
/* Constant to choose between MII0 and MII1 */
#define ICSS_MII0 0
#define ICSS_MII1 1
/* ICSSG_CFG Register bits */
#define ICSSG_CFG_SGMII_MODE BIT(16)
#define ICSSG_CFG_TX_PRU_EN BIT(11)
#define ICSSG_CFG_RX_SFD_TX_SOF_EN BIT(10)
#define ICSSG_CFG_RTU_PRU_PSI_SHARE_EN BIT(9)
#define ICSSG_CFG_IEP1_TX_EN BIT(8)
#define ICSSG_CFG_MII1_MODE GENMASK(6, 5)
#define ICSSG_CFG_MII1_MODE_SHIFT 5
#define ICSSG_CFG_MII0_MODE GENMASK(4, 3)
#define ICSSG_CFG_MII0_MODE_SHIFT 3
#define ICSSG_CFG_RX_L2_G_EN BIT(2)
#define ICSSG_CFG_TX_L2_EN BIT(1)
#define ICSSG_CFG_TX_L1_EN BIT(0)
enum mii_mode {
MII_MODE_MII = 0,
MII_MODE_RGMII
};
/* RGMII CFG Register bits */
#define RGMII_CFG_INBAND_EN_MII0 BIT(16)
#define RGMII_CFG_GIG_EN_MII0 BIT(17)
#define RGMII_CFG_INBAND_EN_MII1 BIT(20)
#define RGMII_CFG_GIG_EN_MII1 BIT(21)
#define RGMII_CFG_FULL_DUPLEX_MII0 BIT(18)
#define RGMII_CFG_FULL_DUPLEX_MII1 BIT(22)
#define RGMII_CFG_SPEED_MII0 GENMASK(2, 1)
#define RGMII_CFG_SPEED_MII1 GENMASK(6, 5)
#define RGMII_CFG_SPEED_MII0_SHIFT 1
#define RGMII_CFG_SPEED_MII1_SHIFT 5
#define RGMII_CFG_FULLDUPLEX_MII0 BIT(3)
#define RGMII_CFG_FULLDUPLEX_MII1 BIT(7)
#define RGMII_CFG_FULLDUPLEX_MII0_SHIFT 3
#define RGMII_CFG_FULLDUPLEX_MII1_SHIFT 7
#define RGMII_CFG_SPEED_10M 0
#define RGMII_CFG_SPEED_100M 1
#define RGMII_CFG_SPEED_1G 2
struct regmap;
struct prueth_emac;
void icssg_mii_update_ipg(struct regmap *mii_rt, int mii, u32 ipg);
void icssg_mii_update_mtu(struct regmap *mii_rt, int mii, int mtu);
void icssg_update_rgmii_cfg(struct regmap *miig_rt, struct prueth_emac *emac);
u32 icssg_rgmii_cfg_get_bitfield(struct regmap *miig_rt, u32 mask, u32 shift);
u32 icssg_rgmii_get_speed(struct regmap *miig_rt, int mii);
u32 icssg_rgmii_get_fullduplex(struct regmap *miig_rt, int mii);
void icssg_miig_set_interface_mode(struct regmap *miig_rt, int mii, phy_interface_t phy_if);
#endif /* __NET_PRUSS_MII_RT_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,527 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Texas Instruments ICSSG Ethernet driver
*
* Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#ifndef __NET_TI_ICSSG_PRUETH_H
#define __NET_TI_ICSSG_PRUETH_H
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/etherdevice.h>
#include <linux/genalloc.h>
#include <linux/if_vlan.h>
#include <linux/if_hsr.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/net_tstamp.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/remoteproc/pruss.h>
#include <linux/pruss_driver.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/remoteproc.h>
#include <linux/dma-mapping.h>
#include <linux/dma/ti-cppi5.h>
#include <linux/dma/k3-udma-glue.h>
#include <net/devlink.h>
#include <net/xdp.h>
#include <net/page_pool/helpers.h>
#include <net/xsk_buff_pool.h>
#include <net/xdp_sock_drv.h>
#include "icssg_config.h"
#include "icss_iep.h"
#include "icssg_switch_map.h"
#define PRUETH_MAX_MTU (2000 - ETH_HLEN - ETH_FCS_LEN)
#define PRUETH_MIN_PKT_SIZE (VLAN_ETH_ZLEN)
#define PRUETH_MAX_PKT_SIZE (PRUETH_MAX_MTU + ETH_HLEN + ETH_FCS_LEN)
#define ICSS_SLICE0 0
#define ICSS_SLICE1 1
#define ICSS_FW_PRU 0
#define ICSS_FW_RTU 1
#define ICSSG_MAX_RFLOWS 8 /* per slice */
#define ICSSG_NUM_PA_STATS 32
#define ICSSG_NUM_MIIG_STATS 60
/* Number of ICSSG related stats */
#define ICSSG_NUM_STATS (ICSSG_NUM_MIIG_STATS + ICSSG_NUM_PA_STATS)
#define ICSSG_NUM_STANDARD_STATS 31
#define ICSSG_NUM_ETHTOOL_STATS (ICSSG_NUM_STATS - ICSSG_NUM_STANDARD_STATS)
#define IEP_DEFAULT_CYCLE_TIME_NS 1000000 /* 1 ms */
#define PRUETH_UNDIRECTED_PKT_DST_TAG 0
#define PRUETH_UNDIRECTED_PKT_TAG_INS BIT(30)
/* Firmware status codes */
#define ICSS_HS_FW_READY 0x55555555
#define ICSS_HS_FW_DEAD 0xDEAD0000 /* lower 16 bits contain error code */
/* Firmware command codes */
#define ICSS_HS_CMD_BUSY 0x40000000
#define ICSS_HS_CMD_DONE 0x80000000
#define ICSS_HS_CMD_CANCEL 0x10000000
/* Firmware commands */
#define ICSS_CMD_SPAD 0x20
#define ICSS_CMD_RXTX 0x10
#define ICSS_CMD_ADD_FDB 0x1
#define ICSS_CMD_DEL_FDB 0x2
#define ICSS_CMD_SET_RUN 0x4
#define ICSS_CMD_GET_FDB_SLOT 0x5
#define ICSS_CMD_ENABLE_VLAN 0x5
#define ICSS_CMD_DISABLE_VLAN 0x6
#define ICSS_CMD_ADD_FILTER 0x7
#define ICSS_CMD_ADD_MAC 0x8
/* VLAN Filtering Related MACROs */
#define PRUETH_DFLT_VLAN_HSR 1
#define PRUETH_DFLT_VLAN_SW 1
#define PRUETH_DFLT_VLAN_MAC 0
#define MAX_VLAN_ID 256
/* In switch mode there are 3 real ports i.e. 3 mac addrs.
* however Linux sees only the host side port. The other 2 ports
* are the switch ports.
* In emac mode there are 2 real ports i.e. 2 mac addrs.
* Linux sees both the ports.
*/
enum prueth_port {
PRUETH_PORT_HOST = 0, /* host side port */
PRUETH_PORT_MII0, /* physical port RG/SG MII 0 */
PRUETH_PORT_MII1, /* physical port RG/SG MII 1 */
PRUETH_PORT_INVALID, /* Invalid prueth port */
};
enum prueth_mac {
PRUETH_MAC0 = 0,
PRUETH_MAC1,
PRUETH_NUM_MACS,
PRUETH_MAC_INVALID,
};
struct prueth_tx_chn {
struct device *dma_dev;
struct napi_struct napi_tx;
struct k3_cppi_desc_pool *desc_pool;
struct k3_udma_glue_tx_channel *tx_chn;
struct prueth_emac *emac;
u32 id;
u32 descs_num;
unsigned int irq;
char name[32];
struct hrtimer tx_hrtimer;
unsigned long tx_pace_timeout_ns;
struct xsk_buff_pool *xsk_pool;
bool irq_disabled;
};
struct prueth_rx_chn {
struct device *dev;
struct device *dma_dev;
struct k3_cppi_desc_pool *desc_pool;
struct k3_udma_glue_rx_channel *rx_chn;
u32 descs_num;
unsigned int irq[ICSSG_MAX_RFLOWS]; /* separate irq per flow */
char name[32];
struct page_pool *pg_pool;
struct xdp_rxq_info xdp_rxq;
struct xsk_buff_pool *xsk_pool;
bool irq_disabled;
};
enum prueth_swdata_type {
PRUETH_SWDATA_INVALID = 0,
PRUETH_SWDATA_SKB,
PRUETH_SWDATA_PAGE,
PRUETH_SWDATA_CMD,
PRUETH_SWDATA_XDPF,
PRUETH_SWDATA_XSK,
};
enum prueth_tx_buff_type {
PRUETH_TX_BUFF_TYPE_XDP_TX,
PRUETH_TX_BUFF_TYPE_XDP_NDO,
};
struct prueth_swdata {
enum prueth_swdata_type type;
union prueth_data {
struct sk_buff *skb;
struct page *page;
u32 cmd;
struct xdp_frame *xdpf;
struct xdp_buff *xdp;
} data;
};
/* There are 4 Tx DMA channels, but the highest priority is CH3 (thread 3)
* and lower three are lower priority channels or threads.
*/
#define PRUETH_MAX_TX_QUEUES 4
#define PRUETH_MAX_TX_TS_REQUESTS 50 /* Max simultaneous TX_TS requests */
/* XDP BPF state */
#define ICSSG_XDP_PASS 0
#define ICSSG_XDP_CONSUMED BIT(0)
#define ICSSG_XDP_TX BIT(1)
#define ICSSG_XDP_REDIR BIT(2)
/* Minimum coalesce time in usecs for both Tx and Rx */
#define ICSSG_MIN_COALESCE_USECS 20
/* data for each emac port */
struct prueth_emac {
bool is_sr1;
struct prueth *prueth;
struct net_device *ndev;
u8 mac_addr[6];
struct napi_struct napi_rx;
u32 msg_enable;
int link;
int speed;
int duplex;
const char *phy_id;
struct device_node *phy_node;
phy_interface_t phy_if;
enum prueth_port port_id;
struct icss_iep *iep;
unsigned int rx_ts_enabled : 1;
unsigned int tx_ts_enabled : 1;
unsigned int half_duplex : 1;
/* DMA related */
struct prueth_tx_chn tx_chns[PRUETH_MAX_TX_QUEUES];
struct completion tdown_complete;
atomic_t tdown_cnt;
struct prueth_rx_chn rx_chns;
int rx_flow_id_base;
int tx_ch_num;
/* SR1.0 Management channel */
struct prueth_rx_chn rx_mgm_chn;
int rx_mgm_flow_id_base;
spinlock_t lock; /* serialize access */
/* TX HW Timestamping */
/* TX TS cookie will be index to the tx_ts_skb array */
struct sk_buff *tx_ts_skb[PRUETH_MAX_TX_TS_REQUESTS];
atomic_t tx_ts_pending;
int tx_ts_irq;
u8 cmd_seq;
/* shutdown related */
__le32 cmd_data[4];
struct completion cmd_complete;
/* Mutex to serialize access to firmware command interface */
struct mutex cmd_lock;
struct work_struct rx_mode_work;
struct pruss_mem_region dram;
bool offload_fwd_mark;
int port_vlan;
struct delayed_work stats_work;
u64 stats[ICSSG_NUM_MIIG_STATS];
u64 pa_stats[ICSSG_NUM_PA_STATS];
/* RX IRQ Coalescing Related */
struct hrtimer rx_hrtimer;
unsigned long rx_pace_timeout_ns;
struct netdev_hw_addr_list vlan_mcast_list[MAX_VLAN_ID];
struct bpf_prog *xdp_prog;
struct xdp_attachment_info xdpi;
int xsk_qid;
};
/* The buf includes headroom compatible with both skb and xdpf */
#define PRUETH_HEADROOM_NA (max(XDP_PACKET_HEADROOM, NET_SKB_PAD) + NET_IP_ALIGN)
#define PRUETH_HEADROOM ALIGN(PRUETH_HEADROOM_NA, sizeof(long))
/**
* struct prueth_pdata - PRUeth platform data
* @fdqring_mode: Free desc queue mode
* @quirk_10m_link_issue: 10M link detect errata
* @switch_mode: switch firmware support
* @banked_ms_ram: banked memory support
*/
struct prueth_pdata {
enum k3_ring_mode fdqring_mode;
u32 quirk_10m_link_issue:1;
u32 switch_mode:1;
u32 banked_ms_ram:1;
};
struct icssg_firmwares {
const char *pru;
const char *rtu;
const char *txpru;
};
/**
* struct prueth - PRUeth structure
* @dev: device
* @pruss: pruss handle
* @pru: rproc instances of PRUs
* @rtu: rproc instances of RTUs
* @txpru: rproc instances of TX_PRUs
* @shram: PRUSS shared RAM region
* @sram_pool: MSMC RAM pool for buffers
* @msmcram: MSMC RAM region
* @eth_node: DT node for the port
* @emac: private EMAC data structure
* @registered_netdevs: list of registered netdevs
* @miig_rt: regmap to mii_g_rt block
* @mii_rt: regmap to mii_rt block
* @pa_stats: regmap to pa_stats block
* @pru_id: ID for each of the PRUs
* @pdev: pointer to ICSSG platform device
* @pdata: pointer to platform data for ICSSG driver
* @icssg_hwcmdseq: seq counter or HWQ messages
* @emacs_initialized: num of EMACs/ext ports that are up/running
* @iep0: pointer to IEP0 device
* @iep1: pointer to IEP1 device
* @vlan_tbl: VLAN-FID table pointer
* @hw_bridge_dev: pointer to HW bridge net device
* @hsr_dev: pointer to the HSR net device
* @hsr_prp_version: enum to store the protocol version of hsr master
* @br_members: bitmask of bridge member ports
* @hsr_members: bitmask of hsr member ports
* @prueth_netdevice_nb: netdevice notifier block
* @prueth_switchdev_nb: switchdev notifier block
* @prueth_switchdev_bl_nb: switchdev blocking notifier block
* @is_switch_mode: flag to indicate if device is in Switch mode
* @is_hsr_offload_mode: flag to indicate if device is in hsr offload mode
* @is_switchmode_supported: indicates platform support for switch mode
* @switch_id: ID for mapping switch ports to bridge
* @default_vlan: Default VLAN for host
* @icssg_emac_firmwares: Firmware names for EMAC mode, indexed per MAC
* @icssg_switch_firmwares: Firmware names for SWITCH mode, indexed per MAC
* @icssg_hsr_firmwares: Firmware names for HSR mode, indexed per MAC
* @icssg_prp_firmwares: Firmware names for PRP mode, indexed per MAC
*/
struct prueth {
struct device *dev;
struct pruss *pruss;
struct rproc *pru[PRUSS_NUM_PRUS];
struct rproc *rtu[PRUSS_NUM_PRUS];
struct rproc *txpru[PRUSS_NUM_PRUS];
struct pruss_mem_region shram;
struct gen_pool *sram_pool;
struct pruss_mem_region msmcram;
struct device_node *eth_node[PRUETH_NUM_MACS];
struct prueth_emac *emac[PRUETH_NUM_MACS];
struct net_device *registered_netdevs[PRUETH_NUM_MACS];
struct regmap *miig_rt;
struct regmap *mii_rt;
struct regmap *pa_stats;
enum pruss_pru_id pru_id[PRUSS_NUM_PRUS];
struct platform_device *pdev;
struct prueth_pdata pdata;
u8 icssg_hwcmdseq;
int emacs_initialized;
struct icss_iep *iep0;
struct icss_iep *iep1;
struct prueth_vlan_tbl *vlan_tbl;
struct net_device *hw_bridge_dev;
struct net_device *hsr_dev;
enum hsr_version hsr_prp_version;
u8 br_members;
u8 hsr_members;
struct notifier_block prueth_netdevice_nb;
struct notifier_block prueth_switchdev_nb;
struct notifier_block prueth_switchdev_bl_nb;
bool is_switch_mode;
bool is_hsr_offload_mode;
bool is_switchmode_supported;
unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
int default_vlan;
/** @vtbl_lock: Lock for vtbl in shared memory */
spinlock_t vtbl_lock;
/** @stats_lock: Lock for reading icssg stats */
spinlock_t stats_lock;
struct icssg_firmwares icssg_emac_firmwares[PRUETH_NUM_MACS];
struct icssg_firmwares icssg_switch_firmwares[PRUETH_NUM_MACS];
struct icssg_firmwares icssg_hsr_firmwares[PRUETH_NUM_MACS];
struct icssg_firmwares icssg_prp_firmwares[PRUETH_NUM_MACS];
};
struct emac_tx_ts_response {
u32 reserved[2];
u32 cookie;
u32 lo_ts;
u32 hi_ts;
};
struct emac_tx_ts_response_sr1 {
__le32 lo_ts;
__le32 hi_ts;
__le32 reserved;
__le32 cookie;
};
/* get PRUSS SLICE number from prueth_emac */
static inline int prueth_emac_slice(struct prueth_emac *emac)
{
switch (emac->port_id) {
case PRUETH_PORT_MII0:
return ICSS_SLICE0;
case PRUETH_PORT_MII1:
return ICSS_SLICE1;
default:
return -EINVAL;
}
}
extern const struct ethtool_ops icssg_ethtool_ops;
extern const struct dev_pm_ops prueth_dev_pm_ops;
static inline u64 icssg_read_time(const void __iomem *addr)
{
u32 low, high;
do {
high = readl(addr + 4);
low = readl(addr);
} while (high != readl(addr + 4));
return low + ((u64)high << 32);
}
/* Classifier helpers */
void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac);
void icssg_class_set_host_mac_addr(struct regmap *miig_rt, const u8 *mac);
void icssg_class_disable(struct regmap *miig_rt, int slice);
void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti,
bool is_sr1);
void icssg_class_promiscuous_sr1(struct regmap *miig_rt, int slice);
void icssg_class_add_mcast_sr1(struct regmap *miig_rt, int slice,
struct net_device *ndev);
void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr);
void icssg_ft3_hsr_configurations(struct regmap *miig_rt, int slice,
struct prueth *prueth);
/* config helpers */
void icssg_config_ipg(struct prueth_emac *emac);
int icssg_config(struct prueth *prueth, struct prueth_emac *emac,
int slice);
int icssg_set_port_state(struct prueth_emac *emac,
enum icssg_port_state_cmd state);
void icssg_config_set_speed(struct prueth_emac *emac);
void icssg_config_half_duplex(struct prueth_emac *emac);
void icssg_init_emac_mode(struct prueth *prueth);
void icssg_init_fw_offload_mode(struct prueth *prueth);
/* Buffer queue helpers */
int icssg_queue_pop(struct prueth *prueth, u8 queue);
void icssg_queue_push(struct prueth *prueth, int queue, u16 addr);
u32 icssg_queue_level(struct prueth *prueth, int queue);
int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
struct mgmt_cmd_rsp *rsp);
int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr,
u8 vid, u8 fid_c2, bool add);
int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr,
u8 vid);
void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask,
u8 untag_mask, bool add);
u16 icssg_get_pvid(struct prueth_emac *emac);
void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port);
int emac_fdb_flow_id_updated(struct prueth_emac *emac);
#define prueth_napi_to_tx_chn(pnapi) \
container_of(pnapi, struct prueth_tx_chn, napi_tx)
void icssg_stats_work_handler(struct work_struct *work);
void emac_update_hardware_stats(struct prueth_emac *emac);
int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name);
/* Common functions */
void prueth_cleanup_rx_chns(struct prueth_emac *emac,
struct prueth_rx_chn *rx_chn,
int max_rflows);
void prueth_cleanup_tx_chns(struct prueth_emac *emac);
void prueth_ndev_del_tx_napi(struct prueth_emac *emac, int num);
void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
struct cppi5_host_desc_t *desc);
int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
int budget, bool *tdown);
int prueth_ndev_add_tx_napi(struct prueth_emac *emac);
int prueth_init_tx_chns(struct prueth_emac *emac);
int prueth_init_rx_chns(struct prueth_emac *emac,
struct prueth_rx_chn *rx_chn,
char *name, u32 max_rflows,
u32 max_desc_num);
int prueth_dma_rx_push_mapped(struct prueth_emac *emac,
struct prueth_rx_chn *rx_chn,
struct page *page, u32 buf_len);
unsigned int prueth_rxbuf_total_len(unsigned int len);
void emac_rx_timestamp(struct prueth_emac *emac,
struct sk_buff *skb, u32 *psdata);
enum netdev_tx icssg_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev);
irqreturn_t prueth_rx_irq(int irq, void *dev_id);
void prueth_cleanup_tx_ts(struct prueth_emac *emac);
int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget);
int prueth_prepare_rx_chan(struct prueth_emac *emac,
struct prueth_rx_chn *chn,
int buf_size);
void prueth_reset_tx_chan(struct prueth_emac *emac, int ch_num,
bool free_skb);
void prueth_reset_rx_chan(struct prueth_rx_chn *chn,
int num_flows, bool disable);
void icssg_ndo_tx_timeout(struct net_device *ndev, unsigned int txqueue);
int icssg_ndo_get_ts_config(struct net_device *ndev,
struct kernel_hwtstamp_config *config);
int icssg_ndo_set_ts_config(struct net_device *ndev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
void icssg_ndo_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats);
int icssg_ndo_get_phys_port_name(struct net_device *ndev, char *name,
size_t len);
int prueth_node_port(struct device_node *eth_node);
int prueth_node_mac(struct device_node *eth_node);
void prueth_netdev_exit(struct prueth *prueth,
struct device_node *eth_node);
int prueth_get_cores(struct prueth *prueth, int slice, bool is_sr1);
void prueth_put_cores(struct prueth *prueth, int slice);
/* Revision specific helper */
u64 icssg_ts_to_ns(u32 hi_sw, u32 hi, u32 lo, u32 cycle_time_ns);
u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
struct xdp_frame *xdpf,
unsigned int q_idx,
enum prueth_tx_buff_type buff_type);
void prueth_rx_cleanup(void *data, dma_addr_t desc_dma);
void prueth_tx_cleanup(void *data, dma_addr_t desc_dma);
int prueth_xsk_wakeup(struct net_device *ndev, u32 qid, u32 flags);
static inline bool prueth_xdp_is_enabled(struct prueth_emac *emac)
{
return !!READ_ONCE(emac->xdp_prog);
}
#endif /* __NET_TI_ICSSG_PRUETH_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
/* ICSSG Buffer queue helpers
*
* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com
*/
#include <linux/regmap.h>
#include "icssg_prueth.h"
#define ICSSG_QUEUES_MAX 64
#define ICSSG_QUEUE_OFFSET 0xd00
#define ICSSG_QUEUE_PEEK_OFFSET 0xe00
#define ICSSG_QUEUE_CNT_OFFSET 0xe40
#define ICSSG_QUEUE_RESET_OFFSET 0xf40
int icssg_queue_pop(struct prueth *prueth, u8 queue)
{
u32 val, cnt;
if (queue >= ICSSG_QUEUES_MAX)
return -EINVAL;
regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4 * queue, &cnt);
if (!cnt)
return -EINVAL;
regmap_read(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, &val);
return val;
}
EXPORT_SYMBOL_GPL(icssg_queue_pop);
void icssg_queue_push(struct prueth *prueth, int queue, u16 addr)
{
if (queue >= ICSSG_QUEUES_MAX)
return;
regmap_write(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, addr);
}
EXPORT_SYMBOL_GPL(icssg_queue_push);
u32 icssg_queue_level(struct prueth *prueth, int queue)
{
u32 reg;
if (queue >= ICSSG_QUEUES_MAX)
return 0;
regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4 * queue, &reg);
return reg;
}
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments ICSSG Ethernet driver
*
* Copyright (C) 2018-2021 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#include "icssg_prueth.h"
#include "icssg_stats.h"
#include <linux/regmap.h>
#define ICSSG_TX_PACKET_OFFSET 0xA0
#define ICSSG_TX_BYTE_OFFSET 0xEC
static u32 stats_base[] = { 0x54c, /* Slice 0 stats start */
0xb18, /* Slice 1 stats start */
};
void emac_update_hardware_stats(struct prueth_emac *emac)
{
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
u32 base = stats_base[slice];
u32 tx_pkt_cnt = 0;
u32 val, reg;
int i;
spin_lock(&prueth->stats_lock);
for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++) {
/* In MII mode TX lines are swapped inside ICSSG, so read Tx stats
* from slice1 for port0 and slice0 for port1 to get accurate Tx
* stats for a given port
*/
if (emac->phy_if == PHY_INTERFACE_MODE_MII &&
icssg_all_miig_stats[i].offset >= ICSSG_TX_PACKET_OFFSET &&
icssg_all_miig_stats[i].offset <= ICSSG_TX_BYTE_OFFSET)
base = stats_base[slice ^ 1];
regmap_read(prueth->miig_rt,
base + icssg_all_miig_stats[i].offset,
&val);
regmap_write(prueth->miig_rt,
base + icssg_all_miig_stats[i].offset,
val);
if (icssg_all_miig_stats[i].offset == ICSSG_TX_PACKET_OFFSET)
tx_pkt_cnt = val;
emac->stats[i] += val;
if (icssg_all_miig_stats[i].offset == ICSSG_TX_BYTE_OFFSET)
emac->stats[i] -= tx_pkt_cnt * 8;
}
if (prueth->pa_stats) {
for (i = 0; i < ARRAY_SIZE(icssg_all_pa_stats); i++) {
reg = icssg_all_pa_stats[i].offset +
slice * sizeof(u32);
regmap_read(prueth->pa_stats, reg, &val);
emac->pa_stats[i] += val;
}
}
spin_unlock(&prueth->stats_lock);
}
void icssg_stats_work_handler(struct work_struct *work)
{
struct prueth_emac *emac = container_of(work, struct prueth_emac,
stats_work.work);
emac_update_hardware_stats(emac);
queue_delayed_work(system_long_wq, &emac->stats_work,
msecs_to_jiffies((STATS_TIME_LIMIT_1G_MS * 1000) / emac->speed));
}
EXPORT_SYMBOL_GPL(icssg_stats_work_handler);
int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name)
{
int i;
for (i = 0; i < ARRAY_SIZE(icssg_all_miig_stats); i++) {
if (!strcmp(icssg_all_miig_stats[i].name, stat_name))
return emac->stats[icssg_all_miig_stats[i].offset / sizeof(u32)];
}
if (emac->prueth->pa_stats) {
for (i = 0; i < ARRAY_SIZE(icssg_all_pa_stats); i++) {
if (!strcmp(icssg_all_pa_stats[i].name, stat_name))
return emac->pa_stats[i];
}
}
netdev_err(emac->ndev, "Invalid stats %s\n", stat_name);
return -EINVAL;
}
@@ -0,0 +1,204 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Texas Instruments ICSSG Ethernet driver
*
* Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#ifndef __NET_TI_ICSSG_STATS_H
#define __NET_TI_ICSSG_STATS_H
#include "icssg_prueth.h"
#define STATS_TIME_LIMIT_1G_MS 25000 /* 25 seconds @ 1G */
struct miig_stats_regs {
/* Rx */
u32 rx_packets;
u32 rx_broadcast_frames;
u32 rx_multicast_frames;
u32 rx_crc_errors;
u32 rx_mii_error_frames;
u32 rx_odd_nibble_frames;
u32 rx_frame_max_size;
u32 rx_max_size_error_frames;
u32 rx_frame_min_size;
u32 rx_min_size_error_frames;
u32 rx_over_errors;
u32 rx_class0_hits;
u32 rx_class1_hits;
u32 rx_class2_hits;
u32 rx_class3_hits;
u32 rx_class4_hits;
u32 rx_class5_hits;
u32 rx_class6_hits;
u32 rx_class7_hits;
u32 rx_class8_hits;
u32 rx_class9_hits;
u32 rx_class10_hits;
u32 rx_class11_hits;
u32 rx_class12_hits;
u32 rx_class13_hits;
u32 rx_class14_hits;
u32 rx_class15_hits;
u32 rx_smd_frags;
u32 rx_bucket1_size;
u32 rx_bucket2_size;
u32 rx_bucket3_size;
u32 rx_bucket4_size;
u32 rx_64B_frames;
u32 rx_bucket1_frames;
u32 rx_bucket2_frames;
u32 rx_bucket3_frames;
u32 rx_bucket4_frames;
u32 rx_bucket5_frames;
u32 rx_bytes;
u32 rx_tx_total_bytes;
/* Tx */
u32 tx_packets;
u32 tx_broadcast_frames;
u32 tx_multicast_frames;
u32 tx_odd_nibble_frames;
u32 tx_underflow_errors;
u32 tx_frame_max_size;
u32 tx_max_size_error_frames;
u32 tx_frame_min_size;
u32 tx_min_size_error_frames;
u32 tx_bucket1_size;
u32 tx_bucket2_size;
u32 tx_bucket3_size;
u32 tx_bucket4_size;
u32 tx_64B_frames;
u32 tx_bucket1_frames;
u32 tx_bucket2_frames;
u32 tx_bucket3_frames;
u32 tx_bucket4_frames;
u32 tx_bucket5_frames;
u32 tx_bytes;
};
#define ICSSG_MIIG_STATS(field, stats_type) \
{ \
#field, \
offsetof(struct miig_stats_regs, field), \
stats_type \
}
struct icssg_miig_stats {
char name[ETH_GSTRING_LEN];
u32 offset;
bool standard_stats;
};
static const struct icssg_miig_stats icssg_all_miig_stats[] = {
/* Rx */
ICSSG_MIIG_STATS(rx_packets, true),
ICSSG_MIIG_STATS(rx_broadcast_frames, false),
ICSSG_MIIG_STATS(rx_multicast_frames, true),
ICSSG_MIIG_STATS(rx_crc_errors, true),
ICSSG_MIIG_STATS(rx_mii_error_frames, false),
ICSSG_MIIG_STATS(rx_odd_nibble_frames, false),
ICSSG_MIIG_STATS(rx_frame_max_size, true),
ICSSG_MIIG_STATS(rx_max_size_error_frames, false),
ICSSG_MIIG_STATS(rx_frame_min_size, true),
ICSSG_MIIG_STATS(rx_min_size_error_frames, false),
ICSSG_MIIG_STATS(rx_over_errors, true),
ICSSG_MIIG_STATS(rx_class0_hits, false),
ICSSG_MIIG_STATS(rx_class1_hits, false),
ICSSG_MIIG_STATS(rx_class2_hits, false),
ICSSG_MIIG_STATS(rx_class3_hits, false),
ICSSG_MIIG_STATS(rx_class4_hits, false),
ICSSG_MIIG_STATS(rx_class5_hits, false),
ICSSG_MIIG_STATS(rx_class6_hits, false),
ICSSG_MIIG_STATS(rx_class7_hits, false),
ICSSG_MIIG_STATS(rx_class8_hits, false),
ICSSG_MIIG_STATS(rx_class9_hits, false),
ICSSG_MIIG_STATS(rx_class10_hits, false),
ICSSG_MIIG_STATS(rx_class11_hits, false),
ICSSG_MIIG_STATS(rx_class12_hits, false),
ICSSG_MIIG_STATS(rx_class13_hits, false),
ICSSG_MIIG_STATS(rx_class14_hits, false),
ICSSG_MIIG_STATS(rx_class15_hits, false),
ICSSG_MIIG_STATS(rx_smd_frags, false),
ICSSG_MIIG_STATS(rx_bucket1_size, true),
ICSSG_MIIG_STATS(rx_bucket2_size, true),
ICSSG_MIIG_STATS(rx_bucket3_size, true),
ICSSG_MIIG_STATS(rx_bucket4_size, true),
ICSSG_MIIG_STATS(rx_64B_frames, true),
ICSSG_MIIG_STATS(rx_bucket1_frames, true),
ICSSG_MIIG_STATS(rx_bucket2_frames, true),
ICSSG_MIIG_STATS(rx_bucket3_frames, true),
ICSSG_MIIG_STATS(rx_bucket4_frames, true),
ICSSG_MIIG_STATS(rx_bucket5_frames, true),
ICSSG_MIIG_STATS(rx_bytes, true),
ICSSG_MIIG_STATS(rx_tx_total_bytes, false),
/* Tx */
ICSSG_MIIG_STATS(tx_packets, true),
ICSSG_MIIG_STATS(tx_broadcast_frames, false),
ICSSG_MIIG_STATS(tx_multicast_frames, false),
ICSSG_MIIG_STATS(tx_odd_nibble_frames, false),
ICSSG_MIIG_STATS(tx_underflow_errors, false),
ICSSG_MIIG_STATS(tx_frame_max_size, true),
ICSSG_MIIG_STATS(tx_max_size_error_frames, false),
ICSSG_MIIG_STATS(tx_frame_min_size, true),
ICSSG_MIIG_STATS(tx_min_size_error_frames, false),
ICSSG_MIIG_STATS(tx_bucket1_size, true),
ICSSG_MIIG_STATS(tx_bucket2_size, true),
ICSSG_MIIG_STATS(tx_bucket3_size, true),
ICSSG_MIIG_STATS(tx_bucket4_size, true),
ICSSG_MIIG_STATS(tx_64B_frames, true),
ICSSG_MIIG_STATS(tx_bucket1_frames, true),
ICSSG_MIIG_STATS(tx_bucket2_frames, true),
ICSSG_MIIG_STATS(tx_bucket3_frames, true),
ICSSG_MIIG_STATS(tx_bucket4_frames, true),
ICSSG_MIIG_STATS(tx_bucket5_frames, true),
ICSSG_MIIG_STATS(tx_bytes, true),
};
#define ICSSG_PA_STATS(field) \
{ \
#field, \
field, \
}
struct icssg_pa_stats {
char name[ETH_GSTRING_LEN];
u32 offset;
};
static const struct icssg_pa_stats icssg_all_pa_stats[] = {
ICSSG_PA_STATS(FW_RTU_PKT_DROP),
ICSSG_PA_STATS(FW_Q0_OVERFLOW),
ICSSG_PA_STATS(FW_Q1_OVERFLOW),
ICSSG_PA_STATS(FW_Q2_OVERFLOW),
ICSSG_PA_STATS(FW_Q3_OVERFLOW),
ICSSG_PA_STATS(FW_Q4_OVERFLOW),
ICSSG_PA_STATS(FW_Q5_OVERFLOW),
ICSSG_PA_STATS(FW_Q6_OVERFLOW),
ICSSG_PA_STATS(FW_Q7_OVERFLOW),
ICSSG_PA_STATS(FW_DROPPED_PKT),
ICSSG_PA_STATS(FW_RX_ERROR),
ICSSG_PA_STATS(FW_RX_DS_INVALID),
ICSSG_PA_STATS(FW_TX_DROPPED_PACKET),
ICSSG_PA_STATS(FW_TX_TS_DROPPED_PACKET),
ICSSG_PA_STATS(FW_INF_PORT_DISABLED),
ICSSG_PA_STATS(FW_INF_SAV),
ICSSG_PA_STATS(FW_INF_SA_DL),
ICSSG_PA_STATS(FW_INF_PORT_BLOCKED),
ICSSG_PA_STATS(FW_INF_DROP_TAGGED),
ICSSG_PA_STATS(FW_INF_DROP_PRIOTAGGED),
ICSSG_PA_STATS(FW_INF_DROP_NOTAG),
ICSSG_PA_STATS(FW_INF_DROP_NOTMEMBER),
ICSSG_PA_STATS(FW_RX_EOF_SHORT_FRMERR),
ICSSG_PA_STATS(FW_RX_B0_DROP_EARLY_EOF),
ICSSG_PA_STATS(FW_TX_JUMBO_FRM_CUTOFF),
ICSSG_PA_STATS(FW_RX_EXP_FRAG_Q_DROP),
ICSSG_PA_STATS(FW_RX_FIFO_OVERRUN),
ICSSG_PA_STATS(FW_CUT_THR_PKT),
ICSSG_PA_STATS(FW_HOST_RX_PKT_CNT),
ICSSG_PA_STATS(FW_HOST_TX_PKT_CNT),
ICSSG_PA_STATS(FW_HOST_EGRESS_Q_PRE_OVERFLOW),
ICSSG_PA_STATS(FW_HOST_EGRESS_Q_EXP_OVERFLOW),
};
#endif /* __NET_TI_ICSSG_STATS_H */
@@ -0,0 +1,270 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Texas Instruments ICSSG Ethernet driver
*
* Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#ifndef __NET_TI_ICSSG_SWITCH_MAP_H
#define __NET_TI_ICSSG_SWITCH_MAP_H
/************************* Ethernet Switch Constants *********************/
/* if bucket size is changed in firmware then this too should be changed
* because it directly impacts FDB ageing calculation
*/
#define NUMBER_OF_FDB_BUCKET_ENTRIES (4)
/* This is fixed in ICSSG */
#define SIZE_OF_FDB (2048)
#define FW_LINK_SPEED_1G (0x00)
#define FW_LINK_SPEED_100M (0x01)
#define FW_LINK_SPEED_10M (0x02)
#define FW_LINK_SPEED_HD (0x80)
/* Time after which FDB entries are checked for aged out values.
* Values are in nanoseconds
*/
#define FDB_AGEING_TIMEOUT_OFFSET 0x0014
/* Default VLAN tag for Host Port */
#define HOST_PORT_DF_VLAN_OFFSET 0x001C
/* Same as HOST_PORT_DF_VLAN_OFFSET */
#define EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET HOST_PORT_DF_VLAN_OFFSET
/* Default VLAN tag for P1 Port */
#define P1_PORT_DF_VLAN_OFFSET 0x0020
/* Same as P1_PORT_DF_VLAN_OFFSET */
#define EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET P1_PORT_DF_VLAN_OFFSET
/* default VLAN tag for P2 Port */
#define P2_PORT_DF_VLAN_OFFSET 0x0024
/* Same as P2_PORT_DF_VLAN_OFFSET */
#define EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET P2_PORT_DF_VLAN_OFFSET
/* VLAN-FID Table offset. 4096 VIDs. 2B per VID = 8KB = 0x2000 */
#define VLAN_STATIC_REG_TABLE_OFFSET 0x0100
/* VLAN-FID Table offset for EMAC */
#define EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET VLAN_STATIC_REG_TABLE_OFFSET
/* Packet descriptor Q reserved memory */
#define PORT_DESC0_HI 0x2104
/* Packet descriptor Q reserved memory */
#define PORT_DESC0_LO 0x2F6C
/* Packet descriptor Q reserved memory */
#define PORT_DESC1_HI 0x3DD4
/* Packet descriptor Q reserved memory */
#define PORT_DESC1_LO 0x4C3C
/* Packet descriptor Q reserved memory */
#define HOST_DESC0_HI 0x5AA4
/* Packet descriptor Q reserved memory */
#define HOST_DESC0_LO 0x5F0C
/* Packet descriptor Q reserved memory */
#define HOST_DESC1_HI 0x6374
/* Packet descriptor Q reserved memory */
#define HOST_DESC1_LO 0x67DC
/* Special packet descriptor Q reserved memory */
#define HOST_SPPD0 0x7AAC
/* Special acket descriptor Q reserved memory */
#define HOST_SPPD1 0x7EAC
/* IEP count cycle counter*/
#define TIMESYNC_FW_WC_CYCLECOUNT_OFFSET 0x83EC
/* IEP count hi roll over count */
#define TIMESYNC_FW_WC_HI_ROLLOVER_COUNT_OFFSET 0x83F4
/* IEP count hi sw counter */
#define TIMESYNC_FW_WC_COUNT_HI_SW_OFFSET_OFFSET 0x83F8
/* Set clock descriptor */
#define TIMESYNC_FW_WC_SETCLOCK_DESC_OFFSET 0x83FC
/* IEP count syncout reduction factor */
#define TIMESYNC_FW_WC_SYNCOUT_REDUCTION_FACTOR_OFFSET 0x843C
/* IEP count syncout reduction counter */
#define TIMESYNC_FW_WC_SYNCOUT_REDUCTION_COUNT_OFFSET 0x8440
/* IEP count syncout start time cycle counter */
#define TIMESYNC_FW_WC_SYNCOUT_START_TIME_CYCLECOUNT_OFFSET 0x8444
/* Control variable to generate SYNC1 */
#define TIMESYNC_FW_WC_ISOM_PIN_SIGNAL_EN_OFFSET 0x844C
/* SystemTime Sync0 periodicity */
#define TIMESYNC_FW_ST_SYNCOUT_PERIOD_OFFSET 0x8450
/* pktTxDelay for P1 = link speed dependent p1 mac delay + p1 phy delay */
#define TIMESYNC_FW_WC_PKTTXDELAY_P1_OFFSET 0x8454
/* pktTxDelay for P2 = link speed dependent p2 mac delay + p2 phy delay */
#define TIMESYNC_FW_WC_PKTTXDELAY_P2_OFFSET 0x8458
/* Set clock operation done signal for next task */
#define TIMESYNC_FW_SIG_PNFW_OFFSET 0x845C
/* Set clock operation done signal for next task */
#define TIMESYNC_FW_SIG_TIMESYNCFW_OFFSET 0x8460
/* New list is copied at this time */
#define TAS_CONFIG_CHANGE_TIME 0x000C
/* config change error counter */
#define TAS_CONFIG_CHANGE_ERROR_COUNTER 0x0014
/* TAS List update pending flag */
#define TAS_CONFIG_PENDING 0x0018
/* TAS list update trigger flag */
#define TAS_CONFIG_CHANGE 0x0019
/* List length for new TAS schedule */
#define TAS_ADMIN_LIST_LENGTH 0x001A
/* Currently active TAS list index */
#define TAS_ACTIVE_LIST_INDEX 0x001B
/* Cycle time for the new TAS schedule */
#define TAS_ADMIN_CYCLE_TIME 0x001C
/* Cycle counts remaining till the TAS list update */
#define TAS_CONFIG_CHANGE_CYCLE_COUNT 0x0020
/* Base Flow ID for sending Packets to Host for Slice0 */
#define PSI_L_REGULAR_FLOW_ID_BASE_OFFSET 0x0024
/* Same as PSI_L_REGULAR_FLOW_ID_BASE_OFFSET */
#define EMAC_ICSSG_SWITCH_PSI_L_REGULAR_FLOW_ID_BASE_OFFSET PSI_L_REGULAR_FLOW_ID_BASE_OFFSET
/* Base Flow ID for sending mgmt and Tx TS to Host for Slice0 */
#define PSI_L_MGMT_FLOW_ID_OFFSET 0x0026
/* Same as PSI_L_MGMT_FLOW_ID_OFFSET */
#define EMAC_ICSSG_SWITCH_PSI_L_MGMT_FLOW_ID_BASE_OFFSET PSI_L_MGMT_FLOW_ID_OFFSET
/* Queue number for Special Packets written here */
#define SPL_PKT_DEFAULT_PRIORITY 0x0028
/* Express Preemptible Queue Mask */
#define EXPRESS_PRE_EMPTIVE_Q_MASK 0x0029
/* Port1/Port2 Default Queue number for untagged Packets, only 1B is used */
#define QUEUE_NUM_UNTAGGED 0x002A
/* Stores the table used for priority regeneration. 1B per PCP/Queue */
#define PORT_Q_PRIORITY_REGEN_OFFSET 0x002C
/* For marking Packet as priority/express (this feature is disabled) or
* cut-through/S&F.
*/
#define EXPRESS_PRE_EMPTIVE_Q_MAP 0x0034
/* Stores the table used for priority mapping. 1B per PCP/Queue */
#define PORT_Q_PRIORITY_MAPPING_OFFSET 0x003C
/* Used to notify the FW of the current link speed */
#define PORT_LINK_SPEED_OFFSET 0x00A8
/* 2k memory pointer reserved for default writes by PRU0*/
#define DEFAULT_MSMC_Q_OFFSET 0x00AC
/* TAS gate mask for windows list0 */
#define TAS_GATE_MASK_LIST0 0x0100
/* TAS gate mask for windows list1 */
#define TAS_GATE_MASK_LIST1 0x0350
/* Memory to Enable/Disable Preemption on TX side */
#define PRE_EMPTION_ENABLE_TX 0x05A0
/* Active State of Preemption on TX side */
#define PRE_EMPTION_ACTIVE_TX 0x05A1
/* Memory to Enable/Disable Verify State Machine Preemption */
#define PRE_EMPTION_ENABLE_VERIFY 0x05A2
/* Verify Status of State Machine */
#define PRE_EMPTION_VERIFY_STATUS 0x05A3
/* Non Final Fragment Size supported by Link Partner */
#define PRE_EMPTION_ADD_FRAG_SIZE_REMOTE 0x05A4
/* Non Final Fragment Size supported by Firmware */
#define PRE_EMPTION_ADD_FRAG_SIZE_LOCAL 0x05A6
/* Time in ms the State machine waits for respond Packet */
#define PRE_EMPTION_VERIFY_TIME 0x05A8
/* Memory used for R30 related management commands */
#define MGR_R30_CMD_OFFSET 0x05AC
/* HW Buffer Pool0 base address */
#define BUFFER_POOL_0_ADDR_OFFSET 0x05BC
/* 16B for Host Egress MSMC Q (Pre-emptible) context */
#define HOST_RX_Q_PRE_CONTEXT_OFFSET 0x0684
/* Buffer for 8 FDB entries to be added by 'Add Multiple FDB entries IOCTL' */
#define FDB_CMD_BUFFER 0x0894
/* TAS queue max sdu length list */
#define TAS_QUEUE_MAX_SDU_LIST 0x08FA
/* Used by FW to generate random number with the SEED value */
#define HD_RAND_SEED_OFFSET 0x0934
/* 16B for Host Egress MSMC Q (Express) context */
#define HOST_RX_Q_EXP_CONTEXT_OFFSET 0x0940
/* Start of 32 bits PA_STAT counters */
#define PA_STAT_32b_START_OFFSET 0x0080
#define FW_RTU_PKT_DROP 0x0088
#define FW_Q0_OVERFLOW 0x0090
#define FW_Q1_OVERFLOW 0x0098
#define FW_Q2_OVERFLOW 0x00A0
#define FW_Q3_OVERFLOW 0x00A8
#define FW_Q4_OVERFLOW 0x00B0
#define FW_Q5_OVERFLOW 0x00B8
#define FW_Q6_OVERFLOW 0x00C0
#define FW_Q7_OVERFLOW 0x00C8
#define FW_DROPPED_PKT 0x00F8
#define FW_RX_ERROR 0x0100
#define FW_RX_DS_INVALID 0x0108
#define FW_TX_DROPPED_PACKET 0x0110
#define FW_TX_TS_DROPPED_PACKET 0x0118
#define FW_INF_PORT_DISABLED 0x0120
#define FW_INF_SAV 0x0128
#define FW_INF_SA_DL 0x0130
#define FW_INF_PORT_BLOCKED 0x0138
#define FW_INF_DROP_TAGGED 0x0140
#define FW_INF_DROP_PRIOTAGGED 0x0148
#define FW_INF_DROP_NOTAG 0x0150
#define FW_INF_DROP_NOTMEMBER 0x0158
#define FW_RX_EOF_SHORT_FRMERR 0x0188
#define FW_RX_B0_DROP_EARLY_EOF 0x0190
#define FW_TX_JUMBO_FRM_CUTOFF 0x0198
#define FW_RX_EXP_FRAG_Q_DROP 0x01A0
#define FW_RX_FIFO_OVERRUN 0x01A8
#define FW_CUT_THR_PKT 0x01B0
#define FW_HOST_RX_PKT_CNT 0x0248
#define FW_HOST_TX_PKT_CNT 0x0250
#define FW_HOST_EGRESS_Q_PRE_OVERFLOW 0x0258
#define FW_HOST_EGRESS_Q_EXP_OVERFLOW 0x0260
#endif /* __NET_TI_ICSSG_SWITCH_MAP_H */
@@ -0,0 +1,477 @@
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments K3 ICSSG Ethernet Switchdev Driver
*
* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/
*
*/
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <net/switchdev.h>
#include "icssg_prueth.h"
#include "icssg_switchdev.h"
#include "icssg_mii_rt.h"
struct prueth_switchdev_event_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct prueth_emac *emac;
unsigned long event;
};
static int prueth_switchdev_stp_state_set(struct prueth_emac *emac,
u8 state)
{
enum icssg_port_state_cmd emac_state;
int ret = 0;
switch (state) {
case BR_STATE_FORWARDING:
emac_state = ICSSG_EMAC_PORT_FORWARD;
break;
case BR_STATE_DISABLED:
emac_state = ICSSG_EMAC_PORT_DISABLE;
break;
case BR_STATE_LISTENING:
case BR_STATE_BLOCKING:
emac_state = ICSSG_EMAC_PORT_BLOCK;
break;
default:
return -EOPNOTSUPP;
}
icssg_set_port_state(emac, emac_state);
netdev_dbg(emac->ndev, "STP state: %u\n", emac_state);
return ret;
}
static int prueth_switchdev_attr_br_flags_set(struct prueth_emac *emac,
struct net_device *orig_dev,
struct switchdev_brport_flags brport_flags)
{
enum icssg_port_state_cmd emac_state;
if (brport_flags.mask & BR_MCAST_FLOOD)
emac_state = ICSSG_EMAC_PORT_MC_FLOODING_ENABLE;
else
emac_state = ICSSG_EMAC_PORT_MC_FLOODING_DISABLE;
netdev_dbg(emac->ndev, "BR_MCAST_FLOOD: %d port %u\n",
emac_state, emac->port_id);
icssg_set_port_state(emac, emac_state);
return 0;
}
static int prueth_switchdev_attr_br_flags_pre_set(struct net_device *netdev,
struct switchdev_brport_flags brport_flags)
{
if (brport_flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD))
return -EINVAL;
return 0;
}
static int prueth_switchdev_attr_set(struct net_device *ndev, const void *ctx,
const struct switchdev_attr *attr,
struct netlink_ext_ack *extack)
{
struct prueth_emac *emac = netdev_priv(ndev);
int ret;
netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, emac->port_id);
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
ret = prueth_switchdev_attr_br_flags_pre_set(ndev,
attr->u.brport_flags);
break;
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
ret = prueth_switchdev_stp_state_set(emac,
attr->u.stp_state);
netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state);
break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
ret = prueth_switchdev_attr_br_flags_set(emac, attr->orig_dev,
attr->u.brport_flags);
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
static void prueth_switchdev_fdb_offload_notify(struct net_device *ndev,
struct switchdev_notifier_fdb_info *rcv)
{
struct switchdev_notifier_fdb_info info;
memset(&info, 0, sizeof(info));
info.addr = rcv->addr;
info.vid = rcv->vid;
info.offloaded = true;
call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
ndev, &info.info, NULL);
}
static void prueth_switchdev_event_work(struct work_struct *work)
{
struct prueth_switchdev_event_work *switchdev_work =
container_of(work, struct prueth_switchdev_event_work, work);
struct prueth_emac *emac = switchdev_work->emac;
struct switchdev_notifier_fdb_info *fdb;
int port_id = emac->port_id;
int ret;
rtnl_lock();
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
fdb = &switchdev_work->fdb_info;
netdev_dbg(emac->ndev, "prueth_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n",
fdb->addr, fdb->vid, fdb->added_by_user,
fdb->offloaded, port_id);
if (!fdb->added_by_user)
break;
if (!ether_addr_equal(emac->mac_addr, fdb->addr))
break;
ret = icssg_fdb_add_del(emac, fdb->addr, fdb->vid,
BIT(port_id), true);
if (!ret)
prueth_switchdev_fdb_offload_notify(emac->ndev, fdb);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
fdb = &switchdev_work->fdb_info;
netdev_dbg(emac->ndev, "prueth_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n",
fdb->addr, fdb->vid, fdb->added_by_user,
fdb->offloaded, port_id);
if (!fdb->added_by_user)
break;
if (!ether_addr_equal(emac->mac_addr, fdb->addr))
break;
icssg_fdb_add_del(emac, fdb->addr, fdb->vid,
BIT(port_id), false);
break;
default:
break;
}
rtnl_unlock();
kfree(switchdev_work->fdb_info.addr);
kfree(switchdev_work);
dev_put(emac->ndev);
}
static int prueth_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
struct prueth_switchdev_event_work *switchdev_work;
struct switchdev_notifier_fdb_info *fdb_info = ptr;
struct prueth_emac *emac = netdev_priv(ndev);
int err;
if (!prueth_dev_check(ndev))
return NOTIFY_DONE;
if (event == SWITCHDEV_PORT_ATTR_SET) {
err = switchdev_handle_port_attr_set(ndev, ptr,
prueth_dev_check,
prueth_switchdev_attr_set);
return notifier_from_errno(err);
}
switchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);
if (WARN_ON(!switchdev_work))
return NOTIFY_BAD;
INIT_WORK(&switchdev_work->work, prueth_switchdev_event_work);
switchdev_work->emac = emac;
switchdev_work->event = event;
switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
case SWITCHDEV_FDB_DEL_TO_DEVICE:
memcpy(&switchdev_work->fdb_info, ptr,
sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
if (!switchdev_work->fdb_info.addr)
goto err_addr_alloc;
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
fdb_info->addr);
dev_hold(ndev);
break;
default:
kfree(switchdev_work);
return NOTIFY_DONE;
}
queue_work(system_long_wq, &switchdev_work->work);
return NOTIFY_DONE;
err_addr_alloc:
kfree(switchdev_work);
return NOTIFY_BAD;
}
static int prueth_switchdev_vlan_add(struct prueth_emac *emac, bool untag, bool pvid,
u8 vid, struct net_device *orig_dev)
{
bool cpu_port = netif_is_bridge_master(orig_dev);
int untag_mask = 0;
int port_mask;
int ret = 0;
if (cpu_port)
port_mask = BIT(PRUETH_PORT_HOST);
else
port_mask = BIT(emac->port_id);
if (untag)
untag_mask = port_mask;
icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true);
netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X PVID %d\n",
vid, port_mask, untag_mask, pvid);
if (!pvid)
return ret;
icssg_set_pvid(emac->prueth, vid, emac->port_id);
return ret;
}
static int prueth_switchdev_vlan_del(struct prueth_emac *emac, u16 vid,
struct net_device *orig_dev)
{
bool cpu_port = netif_is_bridge_master(orig_dev);
int port_mask;
int ret = 0;
if (cpu_port)
port_mask = BIT(PRUETH_PORT_HOST);
else
port_mask = BIT(emac->port_id);
icssg_vtbl_modify(emac, vid, port_mask, 0, false);
if (cpu_port)
icssg_fdb_add_del(emac, emac->mac_addr, vid,
BIT(PRUETH_PORT_HOST), false);
if (vid == icssg_get_pvid(emac))
icssg_set_pvid(emac->prueth, 0, emac->port_id);
netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X\n",
vid, port_mask);
return ret;
}
static int prueth_switchdev_vlans_add(struct prueth_emac *emac,
const struct switchdev_obj_port_vlan *vlan)
{
bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
struct net_device *orig_dev = vlan->obj.orig_dev;
bool cpu_port = netif_is_bridge_master(orig_dev);
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
netdev_dbg(emac->ndev, "VID add vid:%u flags:%X\n",
vlan->vid, vlan->flags);
if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY))
return 0;
if (vlan->vid > 0xff)
return 0;
return prueth_switchdev_vlan_add(emac, untag, pvid, vlan->vid,
orig_dev);
}
static int prueth_switchdev_vlans_del(struct prueth_emac *emac,
const struct switchdev_obj_port_vlan *vlan)
{
if (vlan->vid > 0xff)
return 0;
return prueth_switchdev_vlan_del(emac, vlan->vid,
vlan->obj.orig_dev);
}
static int prueth_switchdev_mdb_add(struct prueth_emac *emac,
struct switchdev_obj_port_mdb *mdb)
{
struct net_device *orig_dev = mdb->obj.orig_dev;
u8 port_mask, fid_c2;
bool cpu_port;
int err;
cpu_port = netif_is_bridge_master(orig_dev);
if (cpu_port)
port_mask = BIT(PRUETH_PORT_HOST);
else
port_mask = BIT(emac->port_id);
fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid);
err = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 | port_mask, true);
netdev_dbg(emac->ndev, "MDB add vid %u:%pM ports: %X\n",
mdb->vid, mdb->addr, port_mask);
return err;
}
static int prueth_switchdev_mdb_del(struct prueth_emac *emac,
struct switchdev_obj_port_mdb *mdb)
{
struct net_device *orig_dev = mdb->obj.orig_dev;
int del_mask, ret, fid_c2;
bool cpu_port;
cpu_port = netif_is_bridge_master(orig_dev);
if (cpu_port)
del_mask = BIT(PRUETH_PORT_HOST);
else
del_mask = BIT(emac->port_id);
fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid);
if (fid_c2 & ~del_mask)
ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 & ~del_mask, true);
else
ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, 0, false);
netdev_dbg(emac->ndev, "MDB del vid %u:%pM ports: %X\n",
mdb->vid, mdb->addr, del_mask);
return ret;
}
static int prueth_switchdev_obj_add(struct net_device *ndev, const void *ctx,
const struct switchdev_obj *obj,
struct netlink_ext_ack *extack)
{
struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
struct prueth_emac *emac = netdev_priv(ndev);
int err = 0;
netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, emac->port_id);
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = prueth_switchdev_vlans_add(emac, vlan);
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
case SWITCHDEV_OBJ_ID_HOST_MDB:
err = prueth_switchdev_mdb_add(emac, mdb);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static int prueth_switchdev_obj_del(struct net_device *ndev, const void *ctx,
const struct switchdev_obj *obj)
{
struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
struct prueth_emac *emac = netdev_priv(ndev);
int err = 0;
netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, emac->port_id);
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = prueth_switchdev_vlans_del(emac, vlan);
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
case SWITCHDEV_OBJ_ID_HOST_MDB:
err = prueth_switchdev_mdb_del(emac, mdb);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static int prueth_switchdev_blocking_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
int err;
switch (event) {
case SWITCHDEV_PORT_OBJ_ADD:
err = switchdev_handle_port_obj_add(dev, ptr,
prueth_dev_check,
prueth_switchdev_obj_add);
return notifier_from_errno(err);
case SWITCHDEV_PORT_OBJ_DEL:
err = switchdev_handle_port_obj_del(dev, ptr,
prueth_dev_check,
prueth_switchdev_obj_del);
return notifier_from_errno(err);
case SWITCHDEV_PORT_ATTR_SET:
err = switchdev_handle_port_attr_set(dev, ptr,
prueth_dev_check,
prueth_switchdev_attr_set);
return notifier_from_errno(err);
default:
break;
}
return NOTIFY_DONE;
}
int prueth_switchdev_register_notifiers(struct prueth *prueth)
{
int ret = 0;
prueth->prueth_switchdev_nb.notifier_call = &prueth_switchdev_event;
ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb);
if (ret) {
dev_err(prueth->dev, "register switchdev notifier fail ret:%d\n",
ret);
return ret;
}
prueth->prueth_switchdev_bl_nb.notifier_call = &prueth_switchdev_blocking_event;
ret = register_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
if (ret) {
dev_err(prueth->dev, "register switchdev blocking notifier ret:%d\n",
ret);
unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
}
return ret;
}
void prueth_switchdev_unregister_notifiers(struct prueth *prueth)
{
unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
}
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/
*/
#ifndef __NET_TI_ICSSG_SWITCHDEV_H
#define __NET_TI_ICSSG_SWITCHDEV_H
#include "icssg_prueth.h"
int prueth_switchdev_register_notifiers(struct prueth *prueth);
void prueth_switchdev_unregister_notifiers(struct prueth *prueth);
bool prueth_dev_check(const struct net_device *ndev);
#endif /* __NET_TI_ICSSG_SWITCHDEV_H */