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