net: ethernet: ti: introduce am65x/j721e gigabit eth subsystem driver

The TI AM65x/J721E SoCs Gigabit Ethernet Switch subsystem (CPSW2G NUSS) has
two ports - One Ethernet port (port 1) with selectable RGMII and RMII
interfaces and an internal Communications Port Programming Interface (CPPI)
port (Host port 0) and with ALE in between. It also contains
 - Management Data Input/Output (MDIO) interface for physical layer device
(PHY) management;
 - Updated Address Lookup Engine (ALE) module;
 - (TBD) New version of Common platform time sync (CPTS) module.

On the TI am65x/J721E SoCs CPSW NUSS Ethernet subsystem into device MCU
domain named MCU_CPSW0.

Host Port 0 CPPI Packet Streaming Interface interface supports 8 TX
channels and one RX channels operating by TI am654 NAVSS Unified DMA
Peripheral Root Complex (UDMA-P) controller.

Introduced driver provides standard Linux net_device to user space and supports:
 - ifconfig up/down
 - MAC address configuration
 - ethtool operation:
   --driver
   --change
   --register-dump
   --negotiate phy
   --statistics
   --set-eee phy
   --show-ring
   --show-channels
   --set-channels
 - net_device ioctl mii-control
 - promisc mode

 - rx checksum offload for non-fragmented IPv4/IPv6 TCP/UDP packets.
   The CPSW NUSS can verify IPv4/IPv6 TCP/UDP packets checksum and fills
   csum information for each packet in psdata[2] word:
   - BIT(16) CHECKSUM_ERROR - indicates csum error
   - BIT(17) FRAGMENT -  indicates fragmented packet
   - BIT(18) TCP_UDP_N -  Indicates TCP packet was detected
   - BIT(19) IPV6_VALID,  BIT(20) IPV4_VALID - indicates IPv6/IPv4 packet
   - BIT(15, 0) CHECKSUM_ADD - This is the value that was summed
   during the checksum computation. This value is FFFFh for non fragmented
   IPV4/6 UDP/TCP packets with no checksum error.

   RX csum offload can be disabled:
	 ethtool -K <dev> rx-checksum on|off

 - tx checksum offload support for IPv4/IPv6 TCP/UDP packets (J721E only).
   TX csum HW offload  can be enabled/disabled:
	 ethtool -K <dev> tx-checksum-ip-generic on|off

 - multiq and switch between round robin/prio modes for cppi tx queues by
   using Netdev private flag "p0-rx-ptype-rrobin" to switch between
   Round Robin and Fixed priority modes:
	 # ethtool --show-priv-flags eth0
	 Private flags for eth0:
	 p0-rx-ptype-rrobin: on
	 # ethtool --set-priv-flags eth0 p0-rx-ptype-rrobin off

   Number of TX DMA channels can be changed using "ethtool -L eth0 tx <N>".

 - GRO support: the napi_gro_receive() and napi_complete_done() are used.

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Tested-by: Murali Karicheri <m-karicheri2@ti.com>
Tested-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Grygorii Strashko 2020-03-24 00:52:49 +02:00 committed by David S. Miller
parent a9a495d582
commit 93a7653031
7 changed files with 3030 additions and 2 deletions

View File

@ -6,7 +6,7 @@
config NET_VENDOR_TI
bool "Texas Instruments (TI) devices"
default y
depends on PCI || EISA || AR7 || ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE
depends on PCI || EISA || AR7 || ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3
---help---
If you have a network (Ethernet) card belonging to this class, say Y.
@ -31,7 +31,7 @@ config TI_DAVINCI_EMAC
config TI_DAVINCI_MDIO
tristate "TI DaVinci MDIO Support"
depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
select PHYLIB
---help---
This driver supports TI's DaVinci MDIO module.
@ -95,6 +95,21 @@ config TI_CPTS_MOD
imply PTP_1588_CLOCK
default m
config TI_K3_AM65_CPSW_NUSS
tristate "TI K3 AM654x/J721E CPSW Ethernet driver"
depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
select TI_DAVINCI_MDIO
imply PHY_TI_GMII_SEL
help
This driver supports TI K3 AM654/J721E CPSW2G Ethernet SubSystem.
The two-port Gigabit Ethernet MAC (MCU_CPSW0) subsystem provides
Ethernet packet communication for the device: One Ethernet port
(port 1) with selectable RGMII and RMII interfaces and an internal
Communications Port Programming Interface (CPPI) port (port 0).
To compile this driver as a module, choose M here: the module
will be called ti-am65-cpsw-nuss.
config TI_KEYSTONE_NETCP
tristate "TI Keystone NETCP Core Support"
select TI_DAVINCI_MDIO

View File

@ -23,3 +23,6 @@ obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o
keystone_netcp-y := netcp_core.o cpsw_ale.o
obj-$(CONFIG_TI_KEYSTONE_NETCP_ETHSS) += keystone_netcp_ethss.o
keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o cpsw_ale.o
obj-$(CONFIG_TI_K3_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o
ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o cpsw_ale.o k3-cppi-desc-pool.o

View File

@ -0,0 +1,747 @@
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver ethtool ops
*
* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
*
*/
#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "am65-cpsw-nuss.h"
#include "cpsw_ale.h"
#define AM65_CPSW_REGDUMP_VER 0x1
enum {
AM65_CPSW_REGDUMP_MOD_NUSS = 1,
AM65_CPSW_REGDUMP_MOD_RGMII_STATUS = 2,
AM65_CPSW_REGDUMP_MOD_MDIO = 3,
AM65_CPSW_REGDUMP_MOD_CPSW = 4,
AM65_CPSW_REGDUMP_MOD_CPSW_P0 = 5,
AM65_CPSW_REGDUMP_MOD_CPSW_P1 = 6,
AM65_CPSW_REGDUMP_MOD_CPSW_CPTS = 7,
AM65_CPSW_REGDUMP_MOD_CPSW_ALE = 8,
AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL = 9,
AM65_CPSW_REGDUMP_MOD_LAST,
};
/**
* struct am65_cpsw_regdump_hdr - regdump record header
*
* @module_id: CPSW module ID
* @len: CPSW module registers space length in u32
*/
struct am65_cpsw_regdump_hdr {
u32 module_id;
u32 len;
};
/**
* struct am65_cpsw_regdump_item - regdump module description
*
* @hdr: CPSW module header
* @start_ofs: CPSW module registers start addr
* @end_ofs: CPSW module registers end addr
*
* Registers dump provided in the format:
* u32 : module ID
* u32 : dump length
* u32[..len]: registers values
*/
struct am65_cpsw_regdump_item {
struct am65_cpsw_regdump_hdr hdr;
u32 start_ofs;
u32 end_ofs;
};
#define AM65_CPSW_REGDUMP_REC(mod, start, end) { \
.hdr.module_id = (mod), \
.hdr.len = (((u32 *)(end)) - ((u32 *)(start)) + 1) * sizeof(u32) * 2 + \
sizeof(struct am65_cpsw_regdump_hdr), \
.start_ofs = (start), \
.end_ofs = end, \
}
static const struct am65_cpsw_regdump_item am65_cpsw_regdump[] = {
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_NUSS, 0x0, 0x1c),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_RGMII_STATUS, 0x30, 0x4c),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_MDIO, 0xf00, 0xffc),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW, 0x20000, 0x2011c),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_P0, 0x21000, 0x21320),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_P1, 0x22000, 0x223a4),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_CPTS,
0x3d000, 0x3d048),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_ALE, 0x3e000, 0x3e13c),
AM65_CPSW_REGDUMP_REC(AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL, 0, 0),
};
struct am65_cpsw_stats_regs {
u32 rx_good_frames;
u32 rx_broadcast_frames;
u32 rx_multicast_frames;
u32 rx_pause_frames; /* slave */
u32 rx_crc_errors;
u32 rx_align_code_errors; /* slave */
u32 rx_oversized_frames;
u32 rx_jabber_frames; /* slave */
u32 rx_undersized_frames;
u32 rx_fragments; /* slave */
u32 ale_drop;
u32 ale_overrun_drop;
u32 rx_octets;
u32 tx_good_frames;
u32 tx_broadcast_frames;
u32 tx_multicast_frames;
u32 tx_pause_frames; /* slave */
u32 tx_deferred_frames; /* slave */
u32 tx_collision_frames; /* slave */
u32 tx_single_coll_frames; /* slave */
u32 tx_mult_coll_frames; /* slave */
u32 tx_excessive_collisions; /* slave */
u32 tx_late_collisions; /* slave */
u32 rx_ipg_error; /* slave 10G only */
u32 tx_carrier_sense_errors; /* slave */
u32 tx_octets;
u32 tx_64B_frames;
u32 tx_65_to_127B_frames;
u32 tx_128_to_255B_frames;
u32 tx_256_to_511B_frames;
u32 tx_512_to_1023B_frames;
u32 tx_1024B_frames;
u32 net_octets;
u32 rx_bottom_fifo_drop;
u32 rx_port_mask_drop;
u32 rx_top_fifo_drop;
u32 ale_rate_limit_drop;
u32 ale_vid_ingress_drop;
u32 ale_da_eq_sa_drop;
u32 ale_block_drop; /* K3 */
u32 ale_secure_drop; /* K3 */
u32 ale_auth_drop; /* K3 */
u32 ale_unknown_ucast;
u32 ale_unknown_ucast_bytes;
u32 ale_unknown_mcast;
u32 ale_unknown_mcast_bytes;
u32 ale_unknown_bcast;
u32 ale_unknown_bcast_bytes;
u32 ale_pol_match;
u32 ale_pol_match_red;
u32 ale_pol_match_yellow;
u32 ale_mcast_sa_drop; /* K3 */
u32 ale_dual_vlan_drop; /* K3 */
u32 ale_len_err_drop; /* K3 */
u32 ale_ip_next_hdr_drop; /* K3 */
u32 ale_ipv4_frag_drop; /* K3 */
u32 __rsvd_1[24];
u32 iet_rx_assembly_err; /* K3 slave */
u32 iet_rx_assembly_ok; /* K3 slave */
u32 iet_rx_smd_err; /* K3 slave */
u32 iet_rx_frag; /* K3 slave */
u32 iet_tx_hold; /* K3 slave */
u32 iet_tx_frag; /* K3 slave */
u32 __rsvd_2[9];
u32 tx_mem_protect_err;
/* following NU only */
u32 tx_pri0;
u32 tx_pri1;
u32 tx_pri2;
u32 tx_pri3;
u32 tx_pri4;
u32 tx_pri5;
u32 tx_pri6;
u32 tx_pri7;
u32 tx_pri0_bcnt;
u32 tx_pri1_bcnt;
u32 tx_pri2_bcnt;
u32 tx_pri3_bcnt;
u32 tx_pri4_bcnt;
u32 tx_pri5_bcnt;
u32 tx_pri6_bcnt;
u32 tx_pri7_bcnt;
u32 tx_pri0_drop;
u32 tx_pri1_drop;
u32 tx_pri2_drop;
u32 tx_pri3_drop;
u32 tx_pri4_drop;
u32 tx_pri5_drop;
u32 tx_pri6_drop;
u32 tx_pri7_drop;
u32 tx_pri0_drop_bcnt;
u32 tx_pri1_drop_bcnt;
u32 tx_pri2_drop_bcnt;
u32 tx_pri3_drop_bcnt;
u32 tx_pri4_drop_bcnt;
u32 tx_pri5_drop_bcnt;
u32 tx_pri6_drop_bcnt;
u32 tx_pri7_drop_bcnt;
};
struct am65_cpsw_ethtool_stat {
char desc[ETH_GSTRING_LEN];
int offset;
};
#define AM65_CPSW_STATS(prefix, field) \
{ \
#prefix#field, \
offsetof(struct am65_cpsw_stats_regs, field) \
}
static const struct am65_cpsw_ethtool_stat am65_host_stats[] = {
AM65_CPSW_STATS(p0_, rx_good_frames),
AM65_CPSW_STATS(p0_, rx_broadcast_frames),
AM65_CPSW_STATS(p0_, rx_multicast_frames),
AM65_CPSW_STATS(p0_, rx_crc_errors),
AM65_CPSW_STATS(p0_, rx_oversized_frames),
AM65_CPSW_STATS(p0_, rx_undersized_frames),
AM65_CPSW_STATS(p0_, ale_drop),
AM65_CPSW_STATS(p0_, ale_overrun_drop),
AM65_CPSW_STATS(p0_, rx_octets),
AM65_CPSW_STATS(p0_, tx_good_frames),
AM65_CPSW_STATS(p0_, tx_broadcast_frames),
AM65_CPSW_STATS(p0_, tx_multicast_frames),
AM65_CPSW_STATS(p0_, tx_octets),
AM65_CPSW_STATS(p0_, tx_64B_frames),
AM65_CPSW_STATS(p0_, tx_65_to_127B_frames),
AM65_CPSW_STATS(p0_, tx_128_to_255B_frames),
AM65_CPSW_STATS(p0_, tx_256_to_511B_frames),
AM65_CPSW_STATS(p0_, tx_512_to_1023B_frames),
AM65_CPSW_STATS(p0_, tx_1024B_frames),
AM65_CPSW_STATS(p0_, net_octets),
AM65_CPSW_STATS(p0_, rx_bottom_fifo_drop),
AM65_CPSW_STATS(p0_, rx_port_mask_drop),
AM65_CPSW_STATS(p0_, rx_top_fifo_drop),
AM65_CPSW_STATS(p0_, ale_rate_limit_drop),
AM65_CPSW_STATS(p0_, ale_vid_ingress_drop),
AM65_CPSW_STATS(p0_, ale_da_eq_sa_drop),
AM65_CPSW_STATS(p0_, ale_block_drop),
AM65_CPSW_STATS(p0_, ale_secure_drop),
AM65_CPSW_STATS(p0_, ale_auth_drop),
AM65_CPSW_STATS(p0_, ale_unknown_ucast),
AM65_CPSW_STATS(p0_, ale_unknown_ucast_bytes),
AM65_CPSW_STATS(p0_, ale_unknown_mcast),
AM65_CPSW_STATS(p0_, ale_unknown_mcast_bytes),
AM65_CPSW_STATS(p0_, ale_unknown_bcast),
AM65_CPSW_STATS(p0_, ale_unknown_bcast_bytes),
AM65_CPSW_STATS(p0_, ale_pol_match),
AM65_CPSW_STATS(p0_, ale_pol_match_red),
AM65_CPSW_STATS(p0_, ale_pol_match_yellow),
AM65_CPSW_STATS(p0_, ale_mcast_sa_drop),
AM65_CPSW_STATS(p0_, ale_dual_vlan_drop),
AM65_CPSW_STATS(p0_, ale_len_err_drop),
AM65_CPSW_STATS(p0_, ale_ip_next_hdr_drop),
AM65_CPSW_STATS(p0_, ale_ipv4_frag_drop),
AM65_CPSW_STATS(p0_, tx_mem_protect_err),
AM65_CPSW_STATS(p0_, tx_pri0),
AM65_CPSW_STATS(p0_, tx_pri1),
AM65_CPSW_STATS(p0_, tx_pri2),
AM65_CPSW_STATS(p0_, tx_pri3),
AM65_CPSW_STATS(p0_, tx_pri4),
AM65_CPSW_STATS(p0_, tx_pri5),
AM65_CPSW_STATS(p0_, tx_pri6),
AM65_CPSW_STATS(p0_, tx_pri7),
AM65_CPSW_STATS(p0_, tx_pri0_bcnt),
AM65_CPSW_STATS(p0_, tx_pri1_bcnt),
AM65_CPSW_STATS(p0_, tx_pri2_bcnt),
AM65_CPSW_STATS(p0_, tx_pri3_bcnt),
AM65_CPSW_STATS(p0_, tx_pri4_bcnt),
AM65_CPSW_STATS(p0_, tx_pri5_bcnt),
AM65_CPSW_STATS(p0_, tx_pri6_bcnt),
AM65_CPSW_STATS(p0_, tx_pri7_bcnt),
AM65_CPSW_STATS(p0_, tx_pri0_drop),
AM65_CPSW_STATS(p0_, tx_pri1_drop),
AM65_CPSW_STATS(p0_, tx_pri2_drop),
AM65_CPSW_STATS(p0_, tx_pri3_drop),
AM65_CPSW_STATS(p0_, tx_pri4_drop),
AM65_CPSW_STATS(p0_, tx_pri5_drop),
AM65_CPSW_STATS(p0_, tx_pri6_drop),
AM65_CPSW_STATS(p0_, tx_pri7_drop),
AM65_CPSW_STATS(p0_, tx_pri0_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri1_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri2_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri3_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri4_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri5_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri6_drop_bcnt),
AM65_CPSW_STATS(p0_, tx_pri7_drop_bcnt),
};
static const struct am65_cpsw_ethtool_stat am65_slave_stats[] = {
AM65_CPSW_STATS(, rx_good_frames),
AM65_CPSW_STATS(, rx_broadcast_frames),
AM65_CPSW_STATS(, rx_multicast_frames),
AM65_CPSW_STATS(, rx_pause_frames),
AM65_CPSW_STATS(, rx_crc_errors),
AM65_CPSW_STATS(, rx_align_code_errors),
AM65_CPSW_STATS(, rx_oversized_frames),
AM65_CPSW_STATS(, rx_jabber_frames),
AM65_CPSW_STATS(, rx_undersized_frames),
AM65_CPSW_STATS(, rx_fragments),
AM65_CPSW_STATS(, ale_drop),
AM65_CPSW_STATS(, ale_overrun_drop),
AM65_CPSW_STATS(, rx_octets),
AM65_CPSW_STATS(, tx_good_frames),
AM65_CPSW_STATS(, tx_broadcast_frames),
AM65_CPSW_STATS(, tx_multicast_frames),
AM65_CPSW_STATS(, tx_pause_frames),
AM65_CPSW_STATS(, tx_deferred_frames),
AM65_CPSW_STATS(, tx_collision_frames),
AM65_CPSW_STATS(, tx_single_coll_frames),
AM65_CPSW_STATS(, tx_mult_coll_frames),
AM65_CPSW_STATS(, tx_excessive_collisions),
AM65_CPSW_STATS(, tx_late_collisions),
AM65_CPSW_STATS(, rx_ipg_error),
AM65_CPSW_STATS(, tx_carrier_sense_errors),
AM65_CPSW_STATS(, tx_octets),
AM65_CPSW_STATS(, tx_64B_frames),
AM65_CPSW_STATS(, tx_65_to_127B_frames),
AM65_CPSW_STATS(, tx_128_to_255B_frames),
AM65_CPSW_STATS(, tx_256_to_511B_frames),
AM65_CPSW_STATS(, tx_512_to_1023B_frames),
AM65_CPSW_STATS(, tx_1024B_frames),
AM65_CPSW_STATS(, net_octets),
AM65_CPSW_STATS(, rx_bottom_fifo_drop),
AM65_CPSW_STATS(, rx_port_mask_drop),
AM65_CPSW_STATS(, rx_top_fifo_drop),
AM65_CPSW_STATS(, ale_rate_limit_drop),
AM65_CPSW_STATS(, ale_vid_ingress_drop),
AM65_CPSW_STATS(, ale_da_eq_sa_drop),
AM65_CPSW_STATS(, ale_block_drop),
AM65_CPSW_STATS(, ale_secure_drop),
AM65_CPSW_STATS(, ale_auth_drop),
AM65_CPSW_STATS(, ale_unknown_ucast),
AM65_CPSW_STATS(, ale_unknown_ucast_bytes),
AM65_CPSW_STATS(, ale_unknown_mcast),
AM65_CPSW_STATS(, ale_unknown_mcast_bytes),
AM65_CPSW_STATS(, ale_unknown_bcast),
AM65_CPSW_STATS(, ale_unknown_bcast_bytes),
AM65_CPSW_STATS(, ale_pol_match),
AM65_CPSW_STATS(, ale_pol_match_red),
AM65_CPSW_STATS(, ale_pol_match_yellow),
AM65_CPSW_STATS(, ale_mcast_sa_drop),
AM65_CPSW_STATS(, ale_dual_vlan_drop),
AM65_CPSW_STATS(, ale_len_err_drop),
AM65_CPSW_STATS(, ale_ip_next_hdr_drop),
AM65_CPSW_STATS(, ale_ipv4_frag_drop),
AM65_CPSW_STATS(, iet_rx_assembly_err),
AM65_CPSW_STATS(, iet_rx_assembly_ok),
AM65_CPSW_STATS(, iet_rx_smd_err),
AM65_CPSW_STATS(, iet_rx_frag),
AM65_CPSW_STATS(, iet_tx_hold),
AM65_CPSW_STATS(, iet_tx_frag),
AM65_CPSW_STATS(, tx_mem_protect_err),
AM65_CPSW_STATS(, tx_pri0),
AM65_CPSW_STATS(, tx_pri1),
AM65_CPSW_STATS(, tx_pri2),
AM65_CPSW_STATS(, tx_pri3),
AM65_CPSW_STATS(, tx_pri4),
AM65_CPSW_STATS(, tx_pri5),
AM65_CPSW_STATS(, tx_pri6),
AM65_CPSW_STATS(, tx_pri7),
AM65_CPSW_STATS(, tx_pri0_bcnt),
AM65_CPSW_STATS(, tx_pri1_bcnt),
AM65_CPSW_STATS(, tx_pri2_bcnt),
AM65_CPSW_STATS(, tx_pri3_bcnt),
AM65_CPSW_STATS(, tx_pri4_bcnt),
AM65_CPSW_STATS(, tx_pri5_bcnt),
AM65_CPSW_STATS(, tx_pri6_bcnt),
AM65_CPSW_STATS(, tx_pri7_bcnt),
AM65_CPSW_STATS(, tx_pri0_drop),
AM65_CPSW_STATS(, tx_pri1_drop),
AM65_CPSW_STATS(, tx_pri2_drop),
AM65_CPSW_STATS(, tx_pri3_drop),
AM65_CPSW_STATS(, tx_pri4_drop),
AM65_CPSW_STATS(, tx_pri5_drop),
AM65_CPSW_STATS(, tx_pri6_drop),
AM65_CPSW_STATS(, tx_pri7_drop),
AM65_CPSW_STATS(, tx_pri0_drop_bcnt),
AM65_CPSW_STATS(, tx_pri1_drop_bcnt),
AM65_CPSW_STATS(, tx_pri2_drop_bcnt),
AM65_CPSW_STATS(, tx_pri3_drop_bcnt),
AM65_CPSW_STATS(, tx_pri4_drop_bcnt),
AM65_CPSW_STATS(, tx_pri5_drop_bcnt),
AM65_CPSW_STATS(, tx_pri6_drop_bcnt),
AM65_CPSW_STATS(, tx_pri7_drop_bcnt),
};
/* Ethtool priv_flags */
static const char am65_cpsw_ethtool_priv_flags[][ETH_GSTRING_LEN] = {
#define AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN BIT(0)
"p0-rx-ptype-rrobin",
};
static int am65_cpsw_ethtool_op_begin(struct net_device *ndev)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
int ret;
ret = pm_runtime_get_sync(common->dev);
if (ret < 0) {
dev_err(common->dev, "ethtool begin failed %d\n", ret);
pm_runtime_put_noidle(common->dev);
}
return ret;
}
static void am65_cpsw_ethtool_op_complete(struct net_device *ndev)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
int ret;
ret = pm_runtime_put(common->dev);
if (ret < 0 && ret != -EBUSY)
dev_err(common->dev, "ethtool complete failed %d\n", ret);
}
static void am65_cpsw_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
strlcpy(info->driver, dev_driver_string(common->dev),
sizeof(info->driver));
strlcpy(info->bus_info, dev_name(common->dev), sizeof(info->bus_info));
}
static u32 am65_cpsw_get_msglevel(struct net_device *ndev)
{
struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev);
return priv->msg_enable;
}
static void am65_cpsw_set_msglevel(struct net_device *ndev, u32 value)
{
struct am65_cpsw_ndev_priv *priv = am65_ndev_to_priv(ndev);
priv->msg_enable = value;
}
static void am65_cpsw_get_channels(struct net_device *ndev,
struct ethtool_channels *ch)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
ch->max_rx = AM65_CPSW_MAX_RX_QUEUES;
ch->max_tx = AM65_CPSW_MAX_TX_QUEUES;
ch->rx_count = AM65_CPSW_MAX_RX_QUEUES;
ch->tx_count = common->tx_ch_num;
}
static int am65_cpsw_set_channels(struct net_device *ndev,
struct ethtool_channels *chs)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
if (!chs->rx_count || !chs->tx_count)
return -EINVAL;
/* Check if interface is up. Can change the num queues when
* the interface is down.
*/
if (netif_running(ndev))
return -EBUSY;
am65_cpsw_nuss_remove_tx_chns(common);
return am65_cpsw_nuss_update_tx_chns(common, chs->tx_count);
}
static void am65_cpsw_get_ringparam(struct net_device *ndev,
struct ethtool_ringparam *ering)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
/* not supported */
ering->tx_pending = common->tx_chns[0].descs_num;
ering->rx_pending = common->rx_chns.descs_num;
}
static void am65_cpsw_get_pauseparam(struct net_device *ndev,
struct ethtool_pauseparam *pause)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
pause->autoneg = AUTONEG_DISABLE;
pause->rx_pause = salve->rx_pause ? true : false;
pause->tx_pause = salve->tx_pause ? true : false;
}
static int am65_cpsw_set_pauseparam(struct net_device *ndev,
struct ethtool_pauseparam *pause)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy)
return -EINVAL;
if (!phy_validate_pause(salve->phy, pause))
return -EINVAL;
salve->rx_pause = pause->rx_pause ? true : false;
salve->tx_pause = pause->tx_pause ? true : false;
phy_set_asym_pause(salve->phy, salve->rx_pause, salve->tx_pause);
return 0;
}
static void am65_cpsw_get_wol(struct net_device *ndev,
struct ethtool_wolinfo *wol)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
wol->supported = 0;
wol->wolopts = 0;
if (salve->phy)
phy_ethtool_get_wol(salve->phy, wol);
}
static int am65_cpsw_set_wol(struct net_device *ndev,
struct ethtool_wolinfo *wol)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy)
return -EOPNOTSUPP;
return phy_ethtool_set_wol(salve->phy, wol);
}
static int am65_cpsw_get_link_ksettings(struct net_device *ndev,
struct ethtool_link_ksettings *ecmd)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy)
return -EOPNOTSUPP;
phy_ethtool_ksettings_get(salve->phy, ecmd);
return 0;
}
static int
am65_cpsw_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *ecmd)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy))
return -EOPNOTSUPP;
return phy_ethtool_ksettings_set(salve->phy, ecmd);
}
static int am65_cpsw_get_eee(struct net_device *ndev, struct ethtool_eee *edata)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy))
return -EOPNOTSUPP;
return phy_ethtool_get_eee(salve->phy, edata);
}
static int am65_cpsw_set_eee(struct net_device *ndev, struct ethtool_eee *edata)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy))
return -EOPNOTSUPP;
return phy_ethtool_set_eee(salve->phy, edata);
}
static int am65_cpsw_nway_reset(struct net_device *ndev)
{
struct am65_cpsw_slave_data *salve = am65_ndev_to_slave(ndev);
if (!salve->phy || phy_is_pseudo_fixed_link(salve->phy))
return -EOPNOTSUPP;
return phy_restart_aneg(salve->phy);
}
static int am65_cpsw_get_regs_len(struct net_device *ndev)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
u32 i, regdump_len = 0;
for (i = 0; i < ARRAY_SIZE(am65_cpsw_regdump); i++) {
if (am65_cpsw_regdump[i].hdr.module_id ==
AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) {
regdump_len += sizeof(struct am65_cpsw_regdump_hdr);
regdump_len += common->ale->params.ale_entries *
ALE_ENTRY_WORDS * sizeof(u32);
continue;
}
regdump_len += am65_cpsw_regdump[i].hdr.len;
}
return regdump_len;
}
static void am65_cpsw_get_regs(struct net_device *ndev,
struct ethtool_regs *regs, void *p)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
u32 i, j, pos, *reg = p;
/* update CPSW IP version */
regs->version = AM65_CPSW_REGDUMP_VER;
pos = 0;
for (i = 0; i < ARRAY_SIZE(am65_cpsw_regdump); i++) {
reg[pos++] = am65_cpsw_regdump[i].hdr.module_id;
if (am65_cpsw_regdump[i].hdr.module_id ==
AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) {
u32 ale_tbl_len = common->ale->params.ale_entries *
ALE_ENTRY_WORDS * sizeof(u32) +
sizeof(struct am65_cpsw_regdump_hdr);
reg[pos++] = ale_tbl_len;
cpsw_ale_dump(common->ale, &reg[pos]);
pos += ale_tbl_len;
continue;
}
reg[pos++] = am65_cpsw_regdump[i].hdr.len;
j = am65_cpsw_regdump[i].start_ofs;
do {
reg[pos++] = j;
reg[pos++] = readl_relaxed(common->ss_base + j);
j += sizeof(u32);
} while (j <= am65_cpsw_regdump[i].end_ofs);
}
}
static int am65_cpsw_get_sset_count(struct net_device *ndev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return ARRAY_SIZE(am65_host_stats) +
ARRAY_SIZE(am65_slave_stats);
case ETH_SS_PRIV_FLAGS:
return ARRAY_SIZE(am65_cpsw_ethtool_priv_flags);
default:
return -EOPNOTSUPP;
}
}
static void am65_cpsw_get_strings(struct net_device *ndev,
u32 stringset, u8 *data)
{
const struct am65_cpsw_ethtool_stat *hw_stats;
u32 i, num_stats;
u8 *p = data;
switch (stringset) {
case ETH_SS_STATS:
num_stats = ARRAY_SIZE(am65_host_stats);
hw_stats = am65_host_stats;
for (i = 0; i < num_stats; i++) {
memcpy(p, hw_stats[i].desc, ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
num_stats = ARRAY_SIZE(am65_slave_stats);
hw_stats = am65_slave_stats;
for (i = 0; i < num_stats; i++) {
memcpy(p, hw_stats[i].desc, ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
break;
case ETH_SS_PRIV_FLAGS:
num_stats = ARRAY_SIZE(am65_cpsw_ethtool_priv_flags);
for (i = 0; i < num_stats; i++) {
memcpy(p, am65_cpsw_ethtool_priv_flags[i],
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
break;
}
}
static void am65_cpsw_get_ethtool_stats(struct net_device *ndev,
struct ethtool_stats *stats, u64 *data)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
const struct am65_cpsw_ethtool_stat *hw_stats;
struct am65_cpsw_host *host_p;
struct am65_cpsw_port *port;
u32 i, num_stats;
host_p = am65_common_get_host(common);
port = am65_ndev_to_port(ndev);
num_stats = ARRAY_SIZE(am65_host_stats);
hw_stats = am65_host_stats;
for (i = 0; i < num_stats; i++)
*data++ = readl_relaxed(host_p->stat_base +
hw_stats[i].offset);
num_stats = ARRAY_SIZE(am65_slave_stats);
hw_stats = am65_slave_stats;
for (i = 0; i < num_stats; i++)
*data++ = readl_relaxed(port->stat_base +
hw_stats[i].offset);
}
static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
u32 priv_flags = 0;
if (common->pf_p0_rx_ptype_rrobin)
priv_flags |= AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN;
return priv_flags;
}
static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
common->pf_p0_rx_ptype_rrobin =
!!(flags & AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN);
am65_cpsw_nuss_set_p0_ptype(common);
return 0;
}
const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
.begin = am65_cpsw_ethtool_op_begin,
.complete = am65_cpsw_ethtool_op_complete,
.get_drvinfo = am65_cpsw_get_drvinfo,
.get_msglevel = am65_cpsw_get_msglevel,
.set_msglevel = am65_cpsw_set_msglevel,
.get_channels = am65_cpsw_get_channels,
.set_channels = am65_cpsw_set_channels,
.get_ringparam = am65_cpsw_get_ringparam,
.get_regs_len = am65_cpsw_get_regs_len,
.get_regs = am65_cpsw_get_regs,
.get_sset_count = am65_cpsw_get_sset_count,
.get_strings = am65_cpsw_get_strings,
.get_ethtool_stats = am65_cpsw_get_ethtool_stats,
.get_ts_info = ethtool_op_get_ts_info,
.get_priv_flags = am65_cpsw_get_ethtool_priv_flags,
.set_priv_flags = am65_cpsw_set_ethtool_priv_flags,
.get_link = ethtool_op_get_link,
.get_link_ksettings = am65_cpsw_get_link_ksettings,
.set_link_ksettings = am65_cpsw_set_link_ksettings,
.get_pauseparam = am65_cpsw_get_pauseparam,
.set_pauseparam = am65_cpsw_set_pauseparam,
.get_wol = am65_cpsw_get_wol,
.set_wol = am65_cpsw_set_wol,
.get_eee = am65_cpsw_get_eee,
.set_eee = am65_cpsw_set_eee,
.nway_reset = am65_cpsw_nway_reset,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
*
*/
#ifndef AM65_CPSW_NUSS_H_
#define AM65_CPSW_NUSS_H_
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#define HOST_PORT_NUM 0
#define AM65_CPSW_MAX_TX_QUEUES 8
#define AM65_CPSW_MAX_RX_QUEUES 1
#define AM65_CPSW_MAX_RX_FLOWS 1
struct am65_cpsw_slave_data {
bool mac_only;
struct cpsw_sl *mac_sl;
struct device_node *phy_node;
struct phy_device *phy;
phy_interface_t phy_if;
struct phy *ifphy;
bool rx_pause;
bool tx_pause;
u8 mac_addr[ETH_ALEN];
};
struct am65_cpsw_port {
struct am65_cpsw_common *common;
struct net_device *ndev;
const char *name;
u32 port_id;
void __iomem *port_base;
void __iomem *stat_base;
bool disabled;
struct am65_cpsw_slave_data slave;
};
struct am65_cpsw_host {
struct am65_cpsw_common *common;
void __iomem *port_base;
void __iomem *stat_base;
};
struct am65_cpsw_tx_chn {
struct napi_struct napi_tx;
struct am65_cpsw_common *common;
struct k3_cppi_desc_pool *desc_pool;
struct k3_udma_glue_tx_channel *tx_chn;
int irq;
u32 id;
u32 descs_num;
char tx_chn_name[128];
};
struct am65_cpsw_rx_chn {
struct device *dev;
struct k3_cppi_desc_pool *desc_pool;
struct k3_udma_glue_rx_channel *rx_chn;
u32 descs_num;
int irq;
};
#define AM65_CPSW_QUIRK_I2027_NO_TX_CSUM BIT(0)
struct am65_cpsw_pdata {
u32 quirks;
};
struct am65_cpsw_common {
struct device *dev;
const struct am65_cpsw_pdata *pdata;
void __iomem *ss_base;
void __iomem *cpsw_base;
u32 port_num;
struct am65_cpsw_host host;
struct am65_cpsw_port *ports;
u32 disabled_ports_mask;
int usage_count; /* number of opened ports */
struct cpsw_ale *ale;
int tx_ch_num;
u32 rx_flow_id_base;
struct am65_cpsw_tx_chn tx_chns[AM65_CPSW_MAX_TX_QUEUES];
struct completion tdown_complete;
atomic_t tdown_cnt;
struct am65_cpsw_rx_chn rx_chns;
struct napi_struct napi_rx;
u32 nuss_ver;
u32 cpsw_ver;
bool pf_p0_rx_ptype_rrobin;
};
struct am65_cpsw_ndev_stats {
u64 tx_packets;
u64 tx_bytes;
u64 rx_packets;
u64 rx_bytes;
struct u64_stats_sync syncp;
};
struct am65_cpsw_ndev_priv {
u32 msg_enable;
struct am65_cpsw_port *port;
struct am65_cpsw_ndev_stats __percpu *stats;
};
#define am65_ndev_to_priv(ndev) \
((struct am65_cpsw_ndev_priv *)netdev_priv(ndev))
#define am65_ndev_to_port(ndev) (am65_ndev_to_priv(ndev)->port)
#define am65_ndev_to_common(ndev) (am65_ndev_to_port(ndev)->common)
#define am65_ndev_to_slave(ndev) (&am65_ndev_to_port(ndev)->slave)
#define am65_common_get_host(common) (&(common)->host)
#define am65_common_get_port(common, id) (&(common)->ports[(id) - 1])
#define am65_cpsw_napi_to_common(pnapi) \
container_of(pnapi, struct am65_cpsw_common, napi_rx)
#define am65_cpsw_napi_to_tx_chn(pnapi) \
container_of(pnapi, struct am65_cpsw_tx_chn, napi_tx)
#define AM65_CPSW_DRV_NAME "am65-cpsw-nuss"
#define AM65_CPSW_IS_CPSW2G(common) ((common)->port_num == 1)
extern const struct ethtool_ops am65_cpsw_ethtool_ops_slave;
void am65_cpsw_nuss_adjust_link(struct net_device *ndev);
void am65_cpsw_nuss_set_p0_ptype(struct am65_cpsw_common *common);
void am65_cpsw_nuss_remove_tx_chns(struct am65_cpsw_common *common);
int am65_cpsw_nuss_update_tx_chns(struct am65_cpsw_common *common, int num_tx);
#endif /* AM65_CPSW_NUSS_H_ */

View File

@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0
/* TI K3 CPPI5 descriptors pool API
*
* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/kernel.h>
#include "k3-cppi-desc-pool.h"
struct k3_cppi_desc_pool {
struct device *dev;
dma_addr_t dma_addr;
void *cpumem; /* dma_alloc map */
size_t desc_size;
size_t mem_size;
size_t num_desc;
struct gen_pool *gen_pool;
};
void k3_cppi_desc_pool_destroy(struct k3_cppi_desc_pool *pool)
{
if (!pool)
return;
WARN(gen_pool_size(pool->gen_pool) != gen_pool_avail(pool->gen_pool),
"k3_knav_desc_pool size %zu != avail %zu",
gen_pool_size(pool->gen_pool),
gen_pool_avail(pool->gen_pool));
if (pool->cpumem)
dma_free_coherent(pool->dev, pool->mem_size, pool->cpumem,
pool->dma_addr);
gen_pool_destroy(pool->gen_pool); /* frees pool->name */
}
struct k3_cppi_desc_pool *
k3_cppi_desc_pool_create_name(struct device *dev, size_t size,
size_t desc_size,
const char *name)
{
struct k3_cppi_desc_pool *pool;
const char *pool_name = NULL;
int ret = -ENOMEM;
pool = devm_kzalloc(dev, sizeof(*pool), GFP_KERNEL);
if (!pool)
return ERR_PTR(ret);
pool->dev = dev;
pool->desc_size = roundup_pow_of_two(desc_size);
pool->num_desc = size;
pool->mem_size = pool->num_desc * pool->desc_size;
pool_name = kstrdup_const(name ? name : dev_name(pool->dev),
GFP_KERNEL);
if (!pool_name)
return ERR_PTR(-ENOMEM);
pool->gen_pool = gen_pool_create(ilog2(pool->desc_size), -1);
if (IS_ERR(pool->gen_pool)) {
ret = PTR_ERR(pool->gen_pool);
dev_err(pool->dev, "pool create failed %d\n", ret);
kfree_const(pool_name);
goto gen_pool_create_fail;
}
pool->gen_pool->name = pool_name;
pool->cpumem = dma_alloc_coherent(pool->dev, pool->mem_size,
&pool->dma_addr, GFP_KERNEL);
if (!pool->cpumem)
goto dma_alloc_fail;
ret = gen_pool_add_virt(pool->gen_pool, (unsigned long)pool->cpumem,
(phys_addr_t)pool->dma_addr, pool->mem_size,
-1);
if (ret < 0) {
dev_err(pool->dev, "pool add failed %d\n", ret);
goto gen_pool_add_virt_fail;
}
return pool;
gen_pool_add_virt_fail:
dma_free_coherent(pool->dev, pool->mem_size, pool->cpumem,
pool->dma_addr);
dma_alloc_fail:
gen_pool_destroy(pool->gen_pool); /* frees pool->name */
gen_pool_create_fail:
devm_kfree(pool->dev, pool);
return ERR_PTR(ret);
}
dma_addr_t k3_cppi_desc_pool_virt2dma(struct k3_cppi_desc_pool *pool,
void *addr)
{
return addr ? pool->dma_addr + (addr - pool->cpumem) : 0;
}
void *k3_cppi_desc_pool_dma2virt(struct k3_cppi_desc_pool *pool, dma_addr_t dma)
{
return dma ? pool->cpumem + (dma - pool->dma_addr) : NULL;
}
void *k3_cppi_desc_pool_alloc(struct k3_cppi_desc_pool *pool)
{
return (void *)gen_pool_alloc(pool->gen_pool, pool->desc_size);
}
void k3_cppi_desc_pool_free(struct k3_cppi_desc_pool *pool, void *addr)
{
gen_pool_free(pool->gen_pool, (unsigned long)addr, pool->desc_size);
}
size_t k3_cppi_desc_pool_avail(struct k3_cppi_desc_pool *pool)
{
return gen_pool_avail(pool->gen_pool) / pool->desc_size;
}

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* TI K3 CPPI5 descriptors pool
*
* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
*/
#ifndef K3_CPPI_DESC_POOL_H_
#define K3_CPPI_DESC_POOL_H_
#include <linux/device.h>
#include <linux/types.h>
struct k3_cppi_desc_pool;
void k3_cppi_desc_pool_destroy(struct k3_cppi_desc_pool *pool);
struct k3_cppi_desc_pool *
k3_cppi_desc_pool_create_name(struct device *dev, size_t size,
size_t desc_size,
const char *name);
#define k3_cppi_desc_pool_create(dev, size, desc_size) \
k3_cppi_desc_pool_create_name(dev, size, desc_size, NULL)
dma_addr_t
k3_cppi_desc_pool_virt2dma(struct k3_cppi_desc_pool *pool, void *addr);
void *
k3_cppi_desc_pool_dma2virt(struct k3_cppi_desc_pool *pool, dma_addr_t dma);
void *k3_cppi_desc_pool_alloc(struct k3_cppi_desc_pool *pool);
void k3_cppi_desc_pool_free(struct k3_cppi_desc_pool *pool, void *addr);
size_t k3_cppi_desc_pool_avail(struct k3_cppi_desc_pool *pool);
#endif /* K3_CPPI_DESC_POOL_H_ */