1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip lan969x Switch driver
4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
9 #define LAN969X_DSM_CAL_DEVS_PER_TAXI 10
10 #define LAN969X_DSM_CAL_TAXIS 5
12 enum lan969x_dsm_cal_dev
{
16 DSM_CAL_DEV_OTHER
, /* 1G or less */
20 /* Each entry in the following struct defines properties for a given speed
21 * (10G, 5G, 2.5G, or 1G or less).
23 struct lan969x_dsm_cal_dev_speed
{
24 /* Number of devices that requires this speed. */
27 /* Array of devices that requires this speed. */
28 u32 devs
[LAN969X_DSM_CAL_DEVS_PER_TAXI
];
30 /* Number of slots required for one device running this speed. */
33 /* Gap between two slots for one device running this speed. */
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 }
46 static int lan969x_dsm_cal_idx_get(u32
*calendar
, u32 cal_len
, u32
*cal_idx
)
48 if (*cal_idx
>= cal_len
)
52 if (calendar
[*cal_idx
] == SPX5_DSM_CAL_EMPTY
)
56 } while (*cal_idx
< cal_len
);
61 static enum lan969x_dsm_cal_dev
lan969x_dsm_cal_get_dev(int speed
)
63 return (speed
== 10000 ? DSM_CAL_DEV_10G
:
64 speed
== 5000 ? DSM_CAL_DEV_5G
:
65 speed
== 2500 ? DSM_CAL_DEV_2G5
:
69 static int lan969x_dsm_cal_get_speed(enum lan969x_dsm_cal_dev dev
)
71 return (dev
== DSM_CAL_DEV_10G
? 10000 :
72 dev
== DSM_CAL_DEV_5G
? 5000 :
73 dev
== DSM_CAL_DEV_2G5
? 2500 :
77 int lan969x_dsm_calendar_calc(struct sparx5
*sparx5
, u32 taxi
,
78 struct sparx5_calendar_data
*data
)
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
;
85 /* Maximum bandwidth for this taxi */
86 taxi_bw
= (128 * 1000000) / sparx5_clk_period(sparx5
->coreclock
);
88 memcpy(data
->taxi_ports
, &lan969x_taxi_ports
[taxi
],
89 LAN969X_DSM_CAL_DEVS_PER_TAXI
* sizeof(u32
));
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
;
95 bw
= sparx5_get_port_cal_speed(sparx5
, portno
);
97 if (portno
< sparx5
->data
->consts
->n_ports_all
)
98 data
->taxi_speeds
[i
] = sparx5_cal_speed_to_value(bw
);
100 data
->taxi_speeds
[i
] = 0;
103 /* Determine the different port types (10G, 5G, 2.5G, <= 1G) in the
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
;
113 required_bw
+= taxi_speed
;
115 dev
= lan969x_dsm_cal_get_dev(taxi_speed
);
116 speed
= &dev_speeds
[dev
];
117 speed
->devs
[speed
->n_devs
++] = i
;
121 if (required_bw
> taxi_bw
) {
122 pr_err("Required bandwidth: %u is higher than total taxi bandwidth: %u",
123 required_bw
, taxi_bw
);
128 data
->schedule
[0] = SPX5_DSM_CAL_EMPTY
;
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
;
140 for (int i
= 0; i
< DSM_CAL_DEV_MAX
; i
++) {
141 speed
= &dev_speeds
[i
];
143 if (speed
->n_devs
== 0)
146 required_bw
= lan969x_dsm_cal_get_speed(i
);
147 speed
->n_slots
= DIV_ROUND_UP(required_bw
, bw_per_slot
);
150 speed
->gap
= DIV_ROUND_UP(cal_len
,
155 n_slots
+= speed
->n_slots
* speed
->n_devs
;
158 if (n_slots
<= cal_len
)
159 break; /* Found a suitable calendar length. */
161 /* Not good enough yet. */
165 if (cal_len
> SPX5_DSM_CAL_LEN
) {
166 pr_err("Invalid length: %u for taxi: %u", cal_len
, taxi
);
170 for (u32 i
= 0; i
< SPX5_DSM_CAL_LEN
; i
++)
171 data
->schedule
[i
] = SPX5_DSM_CAL_EMPTY
;
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
++) {
179 for (n_slots
= 0; n_slots
< speed
->n_slots
; n_slots
++) {
180 err
= lan969x_dsm_cal_idx_get(data
->schedule
,
184 data
->schedule
[idx
] = speed
->devs
[dev
];