1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip lan969x Switch driver 3 * 4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries. 5 */ 6 7 #include "lan969x.h" 8 9 #define LAN969X_DSM_CAL_DEVS_PER_TAXI 10 10 #define LAN969X_DSM_CAL_TAXIS 5 11 12 enum lan969x_dsm_cal_dev { 13 DSM_CAL_DEV_2G5, 14 DSM_CAL_DEV_5G, 15 DSM_CAL_DEV_10G, 16 DSM_CAL_DEV_OTHER, /* 1G or less */ 17 DSM_CAL_DEV_MAX 18 }; 19 20 /* Each entry in the following struct defines properties for a given speed 21 * (10G, 5G, 2.5G, or 1G or less). 22 */ 23 struct lan969x_dsm_cal_dev_speed { 24 /* Number of devices that requires this speed. */ 25 u32 n_devs; 26 27 /* Array of devices that requires this speed. */ 28 u32 devs[LAN969X_DSM_CAL_DEVS_PER_TAXI]; 29 30 /* Number of slots required for one device running this speed. */ 31 u32 n_slots; 32 33 /* Gap between two slots for one device running this speed. */ 34 u32 gap; 35 }; 36 37 static u32 38 lan969x_taxi_ports[LAN969X_DSM_CAL_TAXIS][LAN969X_DSM_CAL_DEVS_PER_TAXI] = { 39 { 0, 4, 1, 2, 3, 5, 6, 7, 28, 29 }, 40 { 8, 12, 9, 13, 10, 11, 14, 15, 99, 99 }, 41 { 16, 20, 17, 21, 18, 19, 22, 23, 99, 99 }, 42 { 24, 25, 99, 99, 99, 99, 99, 99, 99, 99 }, 43 { 26, 27, 99, 99, 99, 99, 99, 99, 99, 99 } 44 }; 45 46 static int lan969x_dsm_cal_idx_get(u32 *calendar, u32 cal_len, u32 *cal_idx) 47 { 48 if (*cal_idx >= cal_len) 49 return -EINVAL; 50 51 do { 52 if (calendar[*cal_idx] == SPX5_DSM_CAL_EMPTY) 53 return 0; 54 55 (*cal_idx)++; 56 } while (*cal_idx < cal_len); 57 58 return -ENOENT; 59 } 60 61 static enum lan969x_dsm_cal_dev lan969x_dsm_cal_get_dev(int speed) 62 { 63 return (speed == 10000 ? DSM_CAL_DEV_10G : 64 speed == 5000 ? DSM_CAL_DEV_5G : 65 speed == 2500 ? DSM_CAL_DEV_2G5 : 66 DSM_CAL_DEV_OTHER); 67 } 68 69 static int lan969x_dsm_cal_get_speed(enum lan969x_dsm_cal_dev dev) 70 { 71 return (dev == DSM_CAL_DEV_10G ? 10000 : 72 dev == DSM_CAL_DEV_5G ? 5000 : 73 dev == DSM_CAL_DEV_2G5 ? 2500 : 74 1000); 75 } 76 77 int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, 78 struct sparx5_calendar_data *data) 79 { 80 struct lan969x_dsm_cal_dev_speed dev_speeds[DSM_CAL_DEV_MAX] = {}; 81 u32 cal_len, n_slots, taxi_bw, n_devs = 0, required_bw = 0; 82 struct lan969x_dsm_cal_dev_speed *speed; 83 int err; 84 85 /* Maximum bandwidth for this taxi */ 86 taxi_bw = (128 * 1000000) / sparx5_clk_period(sparx5->coreclock); 87 88 memcpy(data->taxi_ports, &lan969x_taxi_ports[taxi], 89 LAN969X_DSM_CAL_DEVS_PER_TAXI * sizeof(u32)); 90 91 for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) { 92 u32 portno = data->taxi_ports[i]; 93 enum sparx5_cal_bw bw; 94 95 bw = sparx5_get_port_cal_speed(sparx5, portno); 96 97 if (portno < sparx5->data->consts->n_ports_all) 98 data->taxi_speeds[i] = sparx5_cal_speed_to_value(bw); 99 else 100 data->taxi_speeds[i] = 0; 101 } 102 103 /* Determine the different port types (10G, 5G, 2.5G, <= 1G) in the 104 * this taxi map. 105 */ 106 for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) { 107 u32 taxi_speed = data->taxi_speeds[i]; 108 enum lan969x_dsm_cal_dev dev; 109 110 if (taxi_speed == 0) 111 continue; 112 113 required_bw += taxi_speed; 114 115 dev = lan969x_dsm_cal_get_dev(taxi_speed); 116 speed = &dev_speeds[dev]; 117 speed->devs[speed->n_devs++] = i; 118 n_devs++; 119 } 120 121 if (required_bw > taxi_bw) { 122 pr_err("Required bandwidth: %u is higher than total taxi bandwidth: %u", 123 required_bw, taxi_bw); 124 return -EINVAL; 125 } 126 127 if (n_devs == 0) { 128 data->schedule[0] = SPX5_DSM_CAL_EMPTY; 129 return 0; 130 } 131 132 cal_len = n_devs; 133 134 /* Search for a calendar length that fits all active devices. */ 135 while (cal_len < SPX5_DSM_CAL_LEN) { 136 u32 bw_per_slot = taxi_bw / cal_len; 137 138 n_slots = 0; 139 140 for (int i = 0; i < DSM_CAL_DEV_MAX; i++) { 141 speed = &dev_speeds[i]; 142 143 if (speed->n_devs == 0) 144 continue; 145 146 required_bw = lan969x_dsm_cal_get_speed(i); 147 speed->n_slots = DIV_ROUND_UP(required_bw, bw_per_slot); 148 149 if (speed->n_slots) 150 speed->gap = DIV_ROUND_UP(cal_len, 151 speed->n_slots); 152 else 153 speed->gap = 0; 154 155 n_slots += speed->n_slots * speed->n_devs; 156 } 157 158 if (n_slots <= cal_len) 159 break; /* Found a suitable calendar length. */ 160 161 /* Not good enough yet. */ 162 cal_len = n_slots; 163 } 164 165 if (cal_len > SPX5_DSM_CAL_LEN) { 166 pr_err("Invalid length: %u for taxi: %u", cal_len, taxi); 167 return -EINVAL; 168 } 169 170 for (u32 i = 0; i < SPX5_DSM_CAL_LEN; i++) 171 data->schedule[i] = SPX5_DSM_CAL_EMPTY; 172 173 /* Place the remaining devices */ 174 for (u32 i = 0; i < DSM_CAL_DEV_MAX; i++) { 175 speed = &dev_speeds[i]; 176 for (u32 dev = 0; dev < speed->n_devs; dev++) { 177 u32 idx = 0; 178 179 for (n_slots = 0; n_slots < speed->n_slots; n_slots++) { 180 err = lan969x_dsm_cal_idx_get(data->schedule, 181 cal_len, &idx); 182 if (err) 183 return err; 184 data->schedule[idx] = speed->devs[dev]; 185 idx += speed->gap; 186 } 187 } 188 } 189 190 return 0; 191 } 192