#define RX_TRUNCATE_EN_83XX BIT(4)
#define TX_PAD_EN_838X BIT(5)
-#define SKB_MTU 1600
/* Ethernet header, two stacked VLAN tags (802.1ad QinQ) and FCS */
#define RTETH_FRAME_OVERHEAD (ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN)
+/* Largest frame each family switches, as its datasheet and DSA rmon range have it */
+#define RTETH_838X_MAX_FRAME 10000
+#define RTETH_839X_MAX_FRAME 12288
+#define RTETH_930X_MAX_FRAME 12288
+#define RTETH_931X_MAX_FRAME 12288
/* TODO: change this to 1568 after fragment handling has been tested in the wild */
#define SKB_FRAG_SIZE 400
#define SKB_PAD MAX(32, L1_CACHE_BYTES)
msleep(100);
}
-static void rteth_838x_hw_reset(struct rteth_ctrl *ctrl)
+static void rteth_83xx_set_hol(struct rteth_ctrl *ctrl)
{
- rteth_nic_reset(ctrl, 0xc);
-
/* Free floating rings without space tracking */
regmap_write(ctrl->map, ctrl->r->dma_if_rx_ring_size, 0);
}
+static void rteth_838x_hw_reset(struct rteth_ctrl *ctrl)
+{
+ rteth_nic_reset(ctrl, 0xc);
+}
+
static void rteth_839x_hw_reset(struct rteth_ctrl *ctrl)
{
u32 int_saved, nbuf;
/* Restore notification settings: on RTL838x these bits are null */
regmap_update_bits(ctrl->map, ctrl->r->dma_if_intr_msk, 7 << 20, int_saved & (7 << 20));
regmap_write(ctrl->map, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL, nbuf);
-
- /* Free floating rings without space tracking */
- regmap_write(ctrl->map, ctrl->r->dma_if_rx_ring_size, 0);
}
-static void rteth_93xx_hw_reset(struct rteth_ctrl *ctrl)
+static void rteth_93xx_set_hol(struct rteth_ctrl *ctrl)
{
/*
* The counter registers track the number of packets that are allowed to be appended to
* the ring buffer. On RTL93xx a ring overflow must be avoided at all cost. Defensively
* calculate the space by anticipating that only fully filled packets are received.
*/
- int max_frame_size = SKB_MTU + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + 4;
+ int max_frame_size = ctrl->dev->mtu + RTETH_FRAME_OVERHEAD;
int frags_per_pkt = DIV_ROUND_UP(max_frame_size, SKB_FRAG_SIZE);
int cnt = min(RTETH_RX_RING_SIZE / frags_per_pkt, 0x3ff);
- rteth_nic_reset(ctrl, 0x6);
-
- /* Setup Head of Line */
for (int ring = 0; ring < RTETH_RX_RINGS; ring++) {
int shift = (ring % 3) * 10;
int reg = (ring / 3) * 4;
- u32 v;
regmap_update_bits(ctrl->map, ctrl->r->dma_if_rx_ring_size + reg,
0x3ff << shift, cnt << shift);
+ }
+}
+
+static void rteth_93xx_hw_reset(struct rteth_ctrl *ctrl)
+{
+ rteth_nic_reset(ctrl, 0x6);
+
+ for (int ring = 0; ring < RTETH_RX_RINGS; ring++) {
+ int reg = (ring / 3) * 4;
+ u32 v;
+
/* clear counters by simply writing the current register values back */
regmap_read(ctrl->map, ctrl->r->dma_if_rx_ring_cntr + reg, &v);
regmap_write(ctrl->map, ctrl->r->dma_if_rx_ring_cntr + reg, v);
scoped_guard(spinlock_irqsave, &ctrl->lock) {
ctrl->r->hw_reset(ctrl);
+ ctrl->r->set_hol(ctrl);
rteth_setup_cpu_rx_rings(ctrl);
ret = rteth_setup_ring_buffer(ctrl);
if (ret)
return 0;
}
+static int rteth_change_mtu(struct net_device *dev, int mtu)
+{
+ struct rteth_ctrl *ctrl = netdev_priv(dev);
+ bool grow = mtu > dev->mtu;
+
+ /* A frame of that size must still fit into the linear part and fragments of a single skb */
+ if (mtu + RTETH_FRAME_OVERHEAD > (MAX_SKB_FRAGS + 1) * SKB_FRAG_SIZE)
+ return -EINVAL;
+
+ WRITE_ONCE(dev->mtu, mtu);
+
+ /* Opening the interface programs both limits from the MTU, so leave the hardware alone */
+ if (!netif_running(dev))
+ return 0;
+
+ netdev_warn(dev, "changing the MTU may have unexpected effects under high receive load\n");
+
+ /*
+ * A larger MTU buys a smaller budget, so hand the rings their new limit before the
+ * switch may deliver larger frames, and the other way around when the MTU shrinks.
+ * Hold the lock, so rteth_tx_timeout() cannot reprogram the same registers in between.
+ */
+ scoped_guard(spinlock_irqsave, &ctrl->lock) {
+ if (grow)
+ ctrl->r->set_hol(ctrl);
+
+ rteth_set_max_packet_length(ctrl);
+
+ if (!grow)
+ ctrl->r->set_hol(ctrl);
+ }
+
+ return 0;
+}
+
static void rteth_838x_set_rx_mode(struct net_device *dev)
{
struct rteth_ctrl *ctrl = netdev_priv(dev);
static const struct net_device_ops rteth_838x_netdev_ops = {
.ndo_open = rteth_open,
.ndo_stop = rteth_stop,
+ .ndo_change_mtu = rteth_change_mtu,
.ndo_start_xmit = rteth_start_xmit,
.ndo_set_mac_address = rteth_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
static const struct rteth_config rteth_838x_cfg = {
.cpu_port = RTETH_838X_CPU_PORT,
+ .max_mtu = RTETH_838X_MAX_FRAME - RTETH_FRAME_OVERHEAD,
.rx_rings = 8,
.tx_rx_enable = 0xc,
.tx_trigger_mask = BIT(1),
.hw_stop = &rteth_838x_hw_stop,
.hw_reset = &rteth_838x_hw_reset,
.init_mac = &rteth_838x_init_mac,
+ .set_hol = rteth_83xx_set_hol,
.set_max_packet_length = rteth_838x_set_max_packet_length,
.netdev_ops = &rteth_838x_netdev_ops,
};
static const struct net_device_ops rteth_839x_netdev_ops = {
.ndo_open = rteth_open,
.ndo_stop = rteth_stop,
+ .ndo_change_mtu = rteth_change_mtu,
.ndo_start_xmit = rteth_start_xmit,
.ndo_set_mac_address = rteth_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
static const struct rteth_config rteth_839x_cfg = {
.cpu_port = RTETH_839X_CPU_PORT,
+ .max_mtu = RTETH_839X_MAX_FRAME - RTETH_FRAME_OVERHEAD,
.rx_rings = 8,
.tx_rx_enable = 0xc,
.tx_trigger_mask = BIT(1),
.hw_stop = &rteth_839x_hw_stop,
.hw_reset = &rteth_839x_hw_reset,
.init_mac = &rteth_839x_init_mac,
+ .set_hol = rteth_83xx_set_hol,
.set_max_packet_length = rteth_839x_set_max_packet_length,
.setup_notify_ring_buffer = &rteth_839x_setup_notify_ring_buffer,
.netdev_ops = &rteth_839x_netdev_ops,
static const struct net_device_ops rteth_930x_netdev_ops = {
.ndo_open = rteth_open,
.ndo_stop = rteth_stop,
+ .ndo_change_mtu = rteth_change_mtu,
.ndo_start_xmit = rteth_start_xmit,
.ndo_set_mac_address = rteth_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
static const struct rteth_config rteth_930x_cfg = {
.cpu_port = RTETH_930X_CPU_PORT,
+ .max_mtu = RTETH_930X_MAX_FRAME - RTETH_FRAME_OVERHEAD,
.rx_rings = 32,
.tx_rx_enable = 0x30,
.tx_trigger_mask = GENMASK(3, 2),
.hw_stop = &rteth_930x_hw_stop,
.hw_reset = &rteth_93xx_hw_reset,
.init_mac = &rteth_930x_init_mac,
+ .set_hol = rteth_93xx_set_hol,
.set_max_packet_length = rteth_930x_set_max_packet_length,
.netdev_ops = &rteth_930x_netdev_ops,
};
static const struct net_device_ops rteth_931x_netdev_ops = {
.ndo_open = rteth_open,
.ndo_stop = rteth_stop,
+ .ndo_change_mtu = rteth_change_mtu,
.ndo_start_xmit = rteth_start_xmit,
.ndo_set_mac_address = rteth_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
static const struct rteth_config rteth_931x_cfg = {
.cpu_port = RTETH_931X_CPU_PORT,
+ .max_mtu = RTETH_931X_MAX_FRAME - RTETH_FRAME_OVERHEAD,
.rx_rings = 32,
.tx_rx_enable = 0x30,
.tx_trigger_mask = GENMASK(3, 2),
.hw_stop = &rteth_931x_hw_stop,
.hw_reset = &rteth_93xx_hw_reset,
.init_mac = &rteth_931x_init_mac,
+ .set_hol = rteth_93xx_set_hol,
.set_max_packet_length = rteth_931x_set_max_packet_length,
.netdev_ops = &rteth_931x_netdev_ops,
};
dev->ethtool_ops = &rteth_ethtool_ops;
dev->min_mtu = ETH_ZLEN;
- dev->max_mtu = SKB_MTU;
+ dev->max_mtu = ctrl->r->max_mtu;
dev->features = NETIF_F_RXCSUM;
dev->hw_features = NETIF_F_RXCSUM;
dev->netdev_ops = ctrl->r->netdev_ops;