]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
net: lan969x: add function for calculating the DSM calendar
authorDaniel Machon <daniel.machon@microchip.com>
Wed, 23 Oct 2024 22:01:30 +0000 (00:01 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 31 Oct 2024 01:08:06 +0000 (18:08 -0700)
Lan969x has support for RedBox / HSR / PRP (not implemented yet). In
order to accommodate for this in the future, we need to give lan969x it's
own function for calculating the DSM calendar.

The function calculates the calendar for each taxi bus. The calendar is
used for bandwidth allocation towards the ports attached to the taxi
bus. A calendar configuration consists of up-to 64 slots, which may be
allocated to ports or left unused. Each slot accounts for 1 clock cycle.

Also expose sparx5_cal_speed_to_value(), sparx5_get_port_cal_speed,
sparx5_cal_bw and SPX5_DSM_CAL_EMPTY for use by lan969x.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
Link: https://patch.msgid.link/20241024-sparx5-lan969x-switch-driver-2-v2-11-a0b5fae88a0f@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/microchip/lan969x/Makefile
drivers/net/ethernet/microchip/lan969x/lan969x.c
drivers/net/ethernet/microchip/lan969x/lan969x.h
drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c [new file with mode: 0644]
drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
drivers/net/ethernet/microchip/sparx5/sparx5_main.h

index ff40e7e5d420d57adefaef0d8bd98eb42f266a02..82d318a7219cba06dcf18dcbafef172d345651e7 100644 (file)
@@ -5,7 +5,7 @@
 
 obj-$(CONFIG_LAN969X_SWITCH) += lan969x-switch.o
 
-lan969x-switch-y := lan969x_regs.o lan969x.o
+lan969x-switch-y := lan969x_regs.o lan969x.o lan969x_calendar.o
 
 # Provide include files
 ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/fdma
index a3b40e09b94705e9018167072465ebec212f7779..79e5bcefbd73d84c289db492078a6923ce70f347 100644 (file)
@@ -332,6 +332,7 @@ static const struct sparx5_ops lan969x_ops = {
        .get_sdlb_group          = &lan969x_get_sdlb_group,
        .set_port_mux            = &lan969x_port_mux_set,
        .ptp_irq_handler         = &lan969x_ptp_irq_handler,
+       .dsm_calendar_calc       = &lan969x_dsm_calendar_calc,
 };
 
 const struct sparx5_match_data lan969x_desc = {
index ee890b26ea7926b200fbd8a1fabc9be78864de77..7ce047ad9ca4f66d20c988b67e7182cb01b511b4 100644 (file)
@@ -51,4 +51,7 @@ static inline bool lan969x_port_is_25g(int portno)
        return false;
 }
 
+/* lan969x_calendar.c */
+int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
+                             struct sparx5_calendar_data *data);
 #endif
diff --git a/drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c b/drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c
new file mode 100644 (file)
index 0000000..e857640
--- /dev/null
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip lan969x Switch driver
+ *
+ * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "lan969x.h"
+
+#define LAN969X_DSM_CAL_DEVS_PER_TAXI 10
+#define LAN969X_DSM_CAL_TAXIS 5
+
+enum lan969x_dsm_cal_dev {
+       DSM_CAL_DEV_2G5,
+       DSM_CAL_DEV_5G,
+       DSM_CAL_DEV_10G,
+       DSM_CAL_DEV_OTHER, /* 1G or less */
+       DSM_CAL_DEV_MAX
+};
+
+/* Each entry in the following struct defines properties for a given speed
+ * (10G, 5G, 2.5G, or 1G or less).
+ */
+struct lan969x_dsm_cal_dev_speed {
+       /* Number of devices that requires this speed. */
+       u32 n_devs;
+
+       /* Array of devices that requires this speed. */
+       u32 devs[LAN969X_DSM_CAL_DEVS_PER_TAXI];
+
+       /* Number of slots required for one device running this speed. */
+       u32 n_slots;
+
+       /* Gap between two slots for one device running this speed. */
+       u32 gap;
+};
+
+static u32
+lan969x_taxi_ports[LAN969X_DSM_CAL_TAXIS][LAN969X_DSM_CAL_DEVS_PER_TAXI] = {
+       {  0,  4,  1,  2,  3,  5,  6,  7, 28, 29 },
+       {  8, 12,  9, 13, 10, 11, 14, 15, 99, 99 },
+       { 16, 20, 17, 21, 18, 19, 22, 23, 99, 99 },
+       { 24, 25, 99, 99, 99, 99, 99, 99, 99, 99 },
+       { 26, 27, 99, 99, 99, 99, 99, 99, 99, 99 }
+};
+
+static int lan969x_dsm_cal_idx_get(u32 *calendar, u32 cal_len, u32 *cal_idx)
+{
+       if (*cal_idx >= cal_len)
+               return -EINVAL;
+
+       do {
+               if (calendar[*cal_idx] == SPX5_DSM_CAL_EMPTY)
+                       return 0;
+
+               (*cal_idx)++;
+       } while (*cal_idx < cal_len);
+
+       return -ENOENT;
+}
+
+static enum lan969x_dsm_cal_dev lan969x_dsm_cal_get_dev(int speed)
+{
+       return (speed == 10000 ? DSM_CAL_DEV_10G :
+               speed == 5000  ? DSM_CAL_DEV_5G :
+               speed == 2500  ? DSM_CAL_DEV_2G5 :
+                                DSM_CAL_DEV_OTHER);
+}
+
+static int lan969x_dsm_cal_get_speed(enum lan969x_dsm_cal_dev dev)
+{
+       return (dev == DSM_CAL_DEV_10G ? 10000 :
+               dev == DSM_CAL_DEV_5G  ? 5000 :
+               dev == DSM_CAL_DEV_2G5 ? 2500 :
+                                        1000);
+}
+
+int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
+                             struct sparx5_calendar_data *data)
+{
+       struct lan969x_dsm_cal_dev_speed dev_speeds[DSM_CAL_DEV_MAX] = {};
+       u32 cal_len, n_slots, taxi_bw, n_devs = 0, required_bw  = 0;
+       struct lan969x_dsm_cal_dev_speed *speed;
+       int err;
+
+       /* Maximum bandwidth for this taxi */
+       taxi_bw = (128 * 1000000) / sparx5_clk_period(sparx5->coreclock);
+
+       memcpy(data->taxi_ports, &lan969x_taxi_ports[taxi],
+              LAN969X_DSM_CAL_DEVS_PER_TAXI * sizeof(u32));
+
+       for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) {
+               u32 portno = data->taxi_ports[i];
+               enum sparx5_cal_bw bw;
+
+               bw = sparx5_get_port_cal_speed(sparx5, portno);
+
+               if (portno < sparx5->data->consts->n_ports_all)
+                       data->taxi_speeds[i] = sparx5_cal_speed_to_value(bw);
+               else
+                       data->taxi_speeds[i] = 0;
+       }
+
+       /* Determine the different port types (10G, 5G, 2.5G, <= 1G) in the
+        * this taxi map.
+        */
+       for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) {
+               u32 taxi_speed = data->taxi_speeds[i];
+               enum lan969x_dsm_cal_dev dev;
+
+               if (taxi_speed == 0)
+                       continue;
+
+               required_bw += taxi_speed;
+
+               dev = lan969x_dsm_cal_get_dev(taxi_speed);
+               speed = &dev_speeds[dev];
+               speed->devs[speed->n_devs++] = i;
+               n_devs++;
+       }
+
+       if (required_bw > taxi_bw) {
+               pr_err("Required bandwidth: %u is higher than total taxi bandwidth: %u",
+                      required_bw, taxi_bw);
+               return -EINVAL;
+       }
+
+       if (n_devs == 0) {
+               data->schedule[0] = SPX5_DSM_CAL_EMPTY;
+               return 0;
+       }
+
+       cal_len = n_devs;
+
+       /* Search for a calendar length that fits all active devices. */
+       while (cal_len < SPX5_DSM_CAL_LEN) {
+               u32 bw_per_slot = taxi_bw / cal_len;
+
+               n_slots = 0;
+
+               for (int i = 0; i < DSM_CAL_DEV_MAX; i++) {
+                       speed = &dev_speeds[i];
+
+                       if (speed->n_devs == 0)
+                               continue;
+
+                       required_bw = lan969x_dsm_cal_get_speed(i);
+                       speed->n_slots = DIV_ROUND_UP(required_bw, bw_per_slot);
+
+                       if (speed->n_slots)
+                               speed->gap = DIV_ROUND_UP(cal_len,
+                                                         speed->n_slots);
+                       else
+                               speed->gap = 0;
+
+                       n_slots += speed->n_slots * speed->n_devs;
+               }
+
+               if (n_slots <= cal_len)
+                       break; /* Found a suitable calendar length. */
+
+               /* Not good enough yet. */
+               cal_len = n_slots;
+       }
+
+       if (cal_len > SPX5_DSM_CAL_LEN) {
+               pr_err("Invalid length: %u for taxi: %u", cal_len, taxi);
+               return -EINVAL;
+       }
+
+       for (u32 i = 0; i < SPX5_DSM_CAL_LEN; i++)
+               data->schedule[i] = SPX5_DSM_CAL_EMPTY;
+
+       /* Place the remaining devices */
+       for (u32 i = 0; i < DSM_CAL_DEV_MAX; i++) {
+               speed = &dev_speeds[i];
+               for (u32 dev = 0; dev < speed->n_devs; dev++) {
+                       u32 idx = 0;
+
+                       for (n_slots = 0; n_slots < speed->n_slots; n_slots++) {
+                               err = lan969x_dsm_cal_idx_get(data->schedule,
+                                                             cal_len, &idx);
+                               if (err)
+                                       return err;
+                               data->schedule[idx] = speed->devs[dev];
+                               idx += speed->gap;
+                       }
+               }
+       }
+
+       return 0;
+}
index edc03b6ebf34fdb6f74607cfbe2245a3eb60f21d..64c5ed70cc6b985f1882055aa8faa352c428698d 100644 (file)
@@ -15,7 +15,6 @@
 #define SPX5_CALBITS_PER_PORT          3   /* Bit per port in calendar register */
 
 /* DSM calendar information */
-#define SPX5_DSM_CAL_EMPTY             0xFFFF
 #define SPX5_DSM_CAL_TAXIS             8
 #define SPX5_DSM_CAL_BW_LOSS           553
 
@@ -74,18 +73,6 @@ static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
        }
 }
 
-/* This is used in calendar configuration */
-enum sparx5_cal_bw {
-       SPX5_CAL_SPEED_NONE = 0,
-       SPX5_CAL_SPEED_1G   = 1,
-       SPX5_CAL_SPEED_2G5  = 2,
-       SPX5_CAL_SPEED_5G   = 3,
-       SPX5_CAL_SPEED_10G  = 4,
-       SPX5_CAL_SPEED_25G  = 5,
-       SPX5_CAL_SPEED_0G5  = 6,
-       SPX5_CAL_SPEED_12G5 = 7
-};
-
 static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
 {
        switch (cclock) {
@@ -98,7 +85,7 @@ static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
        return 0;
 }
 
-static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
+u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
 {
        switch (speed) {
        case SPX5_CAL_SPEED_1G:   return 1000;
@@ -111,6 +98,7 @@ static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
        default: return 0;
        }
 }
+EXPORT_SYMBOL_GPL(sparx5_cal_speed_to_value);
 
 static u32 sparx5_bandwidth_to_calendar(u32 bw)
 {
@@ -128,8 +116,7 @@ static u32 sparx5_bandwidth_to_calendar(u32 bw)
        }
 }
 
-static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
-                                                   u32 portno)
+enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno)
 {
        struct sparx5_port *port;
 
@@ -163,6 +150,7 @@ static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
                return SPX5_CAL_SPEED_NONE;
        return sparx5_bandwidth_to_calendar(port->conf.bandwidth);
 }
+EXPORT_SYMBOL_GPL(sparx5_get_port_cal_speed);
 
 /* Auto configure the QSYS calendar based on port configuration */
 int sparx5_config_auto_calendar(struct sparx5 *sparx5)
index 3f66045c57ef86ccf4f09404fee6ae91b33c6e42..1828e2a7d6101d073980484e7f47d42414bbe7a7 100644 (file)
@@ -63,6 +63,18 @@ enum sparx5_vlan_port_type {
        SPX5_VLAN_PORT_TYPE_S_CUSTOM /* S-port using custom type */
 };
 
+/* This is used in calendar configuration */
+enum sparx5_cal_bw {
+       SPX5_CAL_SPEED_NONE = 0,
+       SPX5_CAL_SPEED_1G   = 1,
+       SPX5_CAL_SPEED_2G5  = 2,
+       SPX5_CAL_SPEED_5G   = 3,
+       SPX5_CAL_SPEED_10G  = 4,
+       SPX5_CAL_SPEED_25G  = 5,
+       SPX5_CAL_SPEED_0G5  = 6,
+       SPX5_CAL_SPEED_12G5 = 7
+};
+
 #define SPX5_PORTS             65
 #define SPX5_PORTS_ALL         70 /* Total number of ports */
 
@@ -113,6 +125,7 @@ enum sparx5_vlan_port_type {
 
 #define SPX5_DSM_CAL_LEN               64
 #define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
+#define SPX5_DSM_CAL_EMPTY             0xFFFF
 
 #define SPARX5_MAX_PTP_ID      512
 
@@ -454,6 +467,8 @@ int sparx5_config_auto_calendar(struct sparx5 *sparx5);
 int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
 int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
                             struct sparx5_calendar_data *data);
+u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed);
+enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno);
 
 
 /* sparx5_ethtool.c */