1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 1999 - 2010 Intel Corporation.
4 * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
5 *
6 * This code was derived from the Intel e1000e Linux driver.
7 */
8
9#include "pch_gbe.h"
10#include <linux/module.h> /* for __MODULE_STRING */
11
12#define OPTION_UNSET -1
13#define OPTION_DISABLED 0
14#define OPTION_ENABLED 1
15
16/*
17 * TxDescriptors - Transmit Descriptor Count
18 * @Valid Range: PCH_GBE_MIN_TXD - PCH_GBE_MAX_TXD
19 * @Default Value: PCH_GBE_DEFAULT_TXD
20 */
21static int TxDescriptors = OPTION_UNSET;
22module_param(TxDescriptors, int, 0);
23MODULE_PARM_DESC(TxDescriptors, "Number of transmit descriptors");
24
25/*
26 * RxDescriptors -Receive Descriptor Count
27 * @Valid Range: PCH_GBE_MIN_RXD - PCH_GBE_MAX_RXD
28 * @Default Value: PCH_GBE_DEFAULT_RXD
29 */
30static int RxDescriptors = OPTION_UNSET;
31module_param(RxDescriptors, int, 0);
32MODULE_PARM_DESC(RxDescriptors, "Number of receive descriptors");
33
34/*
35 * Speed - User Specified Speed Override
36 * @Valid Range: 0, 10, 100, 1000
37 * - 0: auto-negotiate at all supported speeds
38 * - 10: only link at 10 Mbps
39 * - 100: only link at 100 Mbps
40 * - 1000: only link at 1000 Mbps
41 * @Default Value: 0
42 */
43static int Speed = OPTION_UNSET;
44module_param(Speed, int, 0);
45MODULE_PARM_DESC(Speed, "Speed setting");
46
47/*
48 * Duplex - User Specified Duplex Override
49 * @Valid Range: 0-2
50 * - 0: auto-negotiate for duplex
51 * - 1: only link at half duplex
52 * - 2: only link at full duplex
53 * @Default Value: 0
54 */
55static int Duplex = OPTION_UNSET;
56module_param(Duplex, int, 0);
57MODULE_PARM_DESC(Duplex, "Duplex setting");
58
59#define HALF_DUPLEX 1
60#define FULL_DUPLEX 2
61
62/*
63 * AutoNeg - Auto-negotiation Advertisement Override
64 * @Valid Range: 0x01-0x0F, 0x20-0x2F
65 *
66 * The AutoNeg value is a bit mask describing which speed and duplex
67 * combinations should be advertised during auto-negotiation.
68 * The supported speed and duplex modes are listed below
69 *
70 * Bit 7 6 5 4 3 2 1 0
71 * Speed (Mbps) N/A N/A 1000 N/A 100 100 10 10
72 * Duplex Full Full Half Full Half
73 *
74 * @Default Value: 0x2F (copper)
75 */
76static int AutoNeg = OPTION_UNSET;
77module_param(AutoNeg, int, 0);
78MODULE_PARM_DESC(AutoNeg, "Advertised auto-negotiation setting");
79
80#define PHY_ADVERTISE_10_HALF 0x0001
81#define PHY_ADVERTISE_10_FULL 0x0002
82#define PHY_ADVERTISE_100_HALF 0x0004
83#define PHY_ADVERTISE_100_FULL 0x0008
84#define PHY_ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */
85#define PHY_ADVERTISE_1000_FULL 0x0020
86#define PCH_AUTONEG_ADVERTISE_DEFAULT 0x2F
87
88/*
89 * FlowControl - User Specified Flow Control Override
90 * @Valid Range: 0-3
91 * - 0: No Flow Control
92 * - 1: Rx only, respond to PAUSE frames but do not generate them
93 * - 2: Tx only, generate PAUSE frames but ignore them on receive
94 * - 3: Full Flow Control Support
95 * @Default Value: Read flow control settings from the EEPROM
96 */
97static int FlowControl = OPTION_UNSET;
98module_param(FlowControl, int, 0);
99MODULE_PARM_DESC(FlowControl, "Flow Control setting");
100
101/*
102 * XsumRX - Receive Checksum Offload Enable/Disable
103 * @Valid Range: 0, 1
104 * - 0: disables all checksum offload
105 * - 1: enables receive IP/TCP/UDP checksum offload
106 * @Default Value: PCH_GBE_DEFAULT_RX_CSUM
107 */
108static int XsumRX = OPTION_UNSET;
109module_param(XsumRX, int, 0);
110MODULE_PARM_DESC(XsumRX, "Disable or enable Receive Checksum offload");
111
112#define PCH_GBE_DEFAULT_RX_CSUM true /* trueorfalse */
113
114/*
115 * XsumTX - Transmit Checksum Offload Enable/Disable
116 * @Valid Range: 0, 1
117 * - 0: disables all checksum offload
118 * - 1: enables transmit IP/TCP/UDP checksum offload
119 * @Default Value: PCH_GBE_DEFAULT_TX_CSUM
120 */
121static int XsumTX = OPTION_UNSET;
122module_param(XsumTX, int, 0);
123MODULE_PARM_DESC(XsumTX, "Disable or enable Transmit Checksum offload");
124
125#define PCH_GBE_DEFAULT_TX_CSUM true /* trueorfalse */
126
127/*
128 * pch_gbe_option - Force the MAC's flow control settings
129 * @hw: Pointer to the HW structure
130 * Returns:
131 * 0: Successful.
132 * Negative value: Failed.
133 */
134struct pch_gbe_option {
135 enum { enable_option, range_option, list_option } type;
136 char *name;
137 char *err;
138 int def;
139 union {
140 struct { /* range_option info */
141 int min;
142 int max;
143 } r;
144 struct { /* list_option info */
145 int nr;
146 const struct pch_gbe_opt_list { int i; char *str; } *p;
147 } l;
148 } arg;
149};
150
151static const struct pch_gbe_opt_list speed_list[] = {
152 { 0, "" },
153 { SPEED_10, "" },
154 { SPEED_100, "" },
155 { SPEED_1000, "" }
156};
157
158static const struct pch_gbe_opt_list dplx_list[] = {
159 { 0, "" },
160 { HALF_DUPLEX, "" },
161 { FULL_DUPLEX, "" }
162};
163
164static const struct pch_gbe_opt_list an_list[] =
165 #define AA "AutoNeg advertising "
166 {{ 0x01, AA "10/HD" },
167 { 0x02, AA "10/FD" },
168 { 0x03, AA "10/FD, 10/HD" },
169 { 0x04, AA "100/HD" },
170 { 0x05, AA "100/HD, 10/HD" },
171 { 0x06, AA "100/HD, 10/FD" },
172 { 0x07, AA "100/HD, 10/FD, 10/HD" },
173 { 0x08, AA "100/FD" },
174 { 0x09, AA "100/FD, 10/HD" },
175 { 0x0a, AA "100/FD, 10/FD" },
176 { 0x0b, AA "100/FD, 10/FD, 10/HD" },
177 { 0x0c, AA "100/FD, 100/HD" },
178 { 0x0d, AA "100/FD, 100/HD, 10/HD" },
179 { 0x0e, AA "100/FD, 100/HD, 10/FD" },
180 { 0x0f, AA "100/FD, 100/HD, 10/FD, 10/HD" },
181 { 0x20, AA "1000/FD" },
182 { 0x21, AA "1000/FD, 10/HD" },
183 { 0x22, AA "1000/FD, 10/FD" },
184 { 0x23, AA "1000/FD, 10/FD, 10/HD" },
185 { 0x24, AA "1000/FD, 100/HD" },
186 { 0x25, AA "1000/FD, 100/HD, 10/HD" },
187 { 0x26, AA "1000/FD, 100/HD, 10/FD" },
188 { 0x27, AA "1000/FD, 100/HD, 10/FD, 10/HD" },
189 { 0x28, AA "1000/FD, 100/FD" },
190 { 0x29, AA "1000/FD, 100/FD, 10/HD" },
191 { 0x2a, AA "1000/FD, 100/FD, 10/FD" },
192 { 0x2b, AA "1000/FD, 100/FD, 10/FD, 10/HD" },
193 { 0x2c, AA "1000/FD, 100/FD, 100/HD" },
194 { 0x2d, AA "1000/FD, 100/FD, 100/HD, 10/HD" },
195 { 0x2e, AA "1000/FD, 100/FD, 100/HD, 10/FD" },
196 { 0x2f, AA "1000/FD, 100/FD, 100/HD, 10/FD, 10/HD" }
197};
198
199static const struct pch_gbe_opt_list fc_list[] = {
200 { PCH_GBE_FC_NONE, "Flow Control Disabled" },
201 { PCH_GBE_FC_RX_PAUSE, "Flow Control Receive Only" },
202 { PCH_GBE_FC_TX_PAUSE, "Flow Control Transmit Only" },
203 { PCH_GBE_FC_FULL, "Flow Control Enabled" }
204};
205
206/**
207 * pch_gbe_validate_option - Validate option
208 * @value: value
209 * @opt: option
210 * @adapter: Board private structure
211 * Returns:
212 * 0: Successful.
213 * Negative value: Failed.
214 */
215static int pch_gbe_validate_option(int *value,
216 const struct pch_gbe_option *opt,
217 struct pch_gbe_adapter *adapter)
218{
219 if (*value == OPTION_UNSET) {
220 *value = opt->def;
221 return 0;
222 }
223
224 switch (opt->type) {
225 case enable_option:
226 switch (*value) {
227 case OPTION_ENABLED:
228 netdev_dbg(adapter->netdev, "%s Enabled\n", opt->name);
229 return 0;
230 case OPTION_DISABLED:
231 netdev_dbg(adapter->netdev, "%s Disabled\n", opt->name);
232 return 0;
233 }
234 break;
235 case range_option:
236 if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
237 netdev_dbg(adapter->netdev, "%s set to %i\n",
238 opt->name, *value);
239 return 0;
240 }
241 break;
242 case list_option: {
243 int i;
244 const struct pch_gbe_opt_list *ent;
245
246 for (i = 0; i < opt->arg.l.nr; i++) {
247 ent = &opt->arg.l.p[i];
248 if (*value == ent->i) {
249 if (ent->str[0] != '\0')
250 netdev_dbg(adapter->netdev, "%s\n",
251 ent->str);
252 return 0;
253 }
254 }
255 }
256 break;
257 default:
258 BUG();
259 }
260
261 netdev_dbg(adapter->netdev, "Invalid %s value specified (%i) %s\n",
262 opt->name, *value, opt->err);
263 *value = opt->def;
264 return -1;
265}
266
267/**
268 * pch_gbe_check_copper_options - Range Checking for Link Options, Copper Version
269 * @adapter: Board private structure
270 */
271static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
272{
273 struct pch_gbe_hw *hw = &adapter->hw;
274 int speed, dplx;
275
276 { /* Speed */
277 static const struct pch_gbe_option opt = {
278 .type = list_option,
279 .name = "Speed",
280 .err = "parameter ignored",
281 .def = 0,
282 .arg = { .l = { .nr = (int)ARRAY_SIZE(speed_list),
283 .p = speed_list } }
284 };
285 speed = Speed;
286 pch_gbe_validate_option(value: &speed, opt: &opt, adapter);
287 }
288 { /* Duplex */
289 static const struct pch_gbe_option opt = {
290 .type = list_option,
291 .name = "Duplex",
292 .err = "parameter ignored",
293 .def = 0,
294 .arg = { .l = { .nr = (int)ARRAY_SIZE(dplx_list),
295 .p = dplx_list } }
296 };
297 dplx = Duplex;
298 pch_gbe_validate_option(value: &dplx, opt: &opt, adapter);
299 }
300
301 { /* Autoneg */
302 static const struct pch_gbe_option opt = {
303 .type = list_option,
304 .name = "AutoNeg",
305 .err = "parameter ignored",
306 .def = PCH_AUTONEG_ADVERTISE_DEFAULT,
307 .arg = { .l = { .nr = (int)ARRAY_SIZE(an_list),
308 .p = an_list} }
309 };
310 if (speed || dplx) {
311 netdev_dbg(adapter->netdev,
312 "AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n");
313 hw->phy.autoneg_advertised = opt.def;
314 } else {
315 int tmp = AutoNeg;
316
317 pch_gbe_validate_option(value: &tmp, opt: &opt, adapter);
318 hw->phy.autoneg_advertised = tmp;
319 }
320 }
321
322 switch (speed + dplx) {
323 case 0:
324 hw->mac.autoneg = hw->mac.fc_autoneg = 1;
325 if ((speed || dplx))
326 netdev_dbg(adapter->netdev,
327 "Speed and duplex autonegotiation enabled\n");
328 hw->mac.link_speed = SPEED_10;
329 hw->mac.link_duplex = DUPLEX_HALF;
330 break;
331 case HALF_DUPLEX:
332 netdev_dbg(adapter->netdev,
333 "Half Duplex specified without Speed\n");
334 netdev_dbg(adapter->netdev,
335 "Using Autonegotiation at Half Duplex only\n");
336 hw->mac.autoneg = hw->mac.fc_autoneg = 1;
337 hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF |
338 PHY_ADVERTISE_100_HALF;
339 hw->mac.link_speed = SPEED_10;
340 hw->mac.link_duplex = DUPLEX_HALF;
341 break;
342 case FULL_DUPLEX:
343 netdev_dbg(adapter->netdev,
344 "Full Duplex specified without Speed\n");
345 netdev_dbg(adapter->netdev,
346 "Using Autonegotiation at Full Duplex only\n");
347 hw->mac.autoneg = hw->mac.fc_autoneg = 1;
348 hw->phy.autoneg_advertised = PHY_ADVERTISE_10_FULL |
349 PHY_ADVERTISE_100_FULL |
350 PHY_ADVERTISE_1000_FULL;
351 hw->mac.link_speed = SPEED_10;
352 hw->mac.link_duplex = DUPLEX_FULL;
353 break;
354 case SPEED_10:
355 netdev_dbg(adapter->netdev,
356 "10 Mbps Speed specified without Duplex\n");
357 netdev_dbg(adapter->netdev,
358 "Using Autonegotiation at 10 Mbps only\n");
359 hw->mac.autoneg = hw->mac.fc_autoneg = 1;
360 hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF |
361 PHY_ADVERTISE_10_FULL;
362 hw->mac.link_speed = SPEED_10;
363 hw->mac.link_duplex = DUPLEX_HALF;
364 break;
365 case SPEED_10 + HALF_DUPLEX:
366 netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Half Duplex\n");
367 hw->mac.autoneg = hw->mac.fc_autoneg = 0;
368 hw->phy.autoneg_advertised = 0;
369 hw->mac.link_speed = SPEED_10;
370 hw->mac.link_duplex = DUPLEX_HALF;
371 break;
372 case SPEED_10 + FULL_DUPLEX:
373 netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Full Duplex\n");
374 hw->mac.autoneg = hw->mac.fc_autoneg = 0;
375 hw->phy.autoneg_advertised = 0;
376 hw->mac.link_speed = SPEED_10;
377 hw->mac.link_duplex = DUPLEX_FULL;
378 break;
379 case SPEED_100:
380 netdev_dbg(adapter->netdev,
381 "100 Mbps Speed specified without Duplex\n");
382 netdev_dbg(adapter->netdev,
383 "Using Autonegotiation at 100 Mbps only\n");
384 hw->mac.autoneg = hw->mac.fc_autoneg = 1;
385 hw->phy.autoneg_advertised = PHY_ADVERTISE_100_HALF |
386 PHY_ADVERTISE_100_FULL;
387 hw->mac.link_speed = SPEED_100;
388 hw->mac.link_duplex = DUPLEX_HALF;
389 break;
390 case SPEED_100 + HALF_DUPLEX:
391 netdev_dbg(adapter->netdev,
392 "Forcing to 100 Mbps Half Duplex\n");
393 hw->mac.autoneg = hw->mac.fc_autoneg = 0;
394 hw->phy.autoneg_advertised = 0;
395 hw->mac.link_speed = SPEED_100;
396 hw->mac.link_duplex = DUPLEX_HALF;
397 break;
398 case SPEED_100 + FULL_DUPLEX:
399 netdev_dbg(adapter->netdev,
400 "Forcing to 100 Mbps Full Duplex\n");
401 hw->mac.autoneg = hw->mac.fc_autoneg = 0;
402 hw->phy.autoneg_advertised = 0;
403 hw->mac.link_speed = SPEED_100;
404 hw->mac.link_duplex = DUPLEX_FULL;
405 break;
406 case SPEED_1000:
407 netdev_dbg(adapter->netdev,
408 "1000 Mbps Speed specified without Duplex\n");
409 goto full_duplex_only;
410 case SPEED_1000 + HALF_DUPLEX:
411 netdev_dbg(adapter->netdev,
412 "Half Duplex is not supported at 1000 Mbps\n");
413 fallthrough;
414 case SPEED_1000 + FULL_DUPLEX:
415full_duplex_only:
416 netdev_dbg(adapter->netdev,
417 "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
418 hw->mac.autoneg = hw->mac.fc_autoneg = 1;
419 hw->phy.autoneg_advertised = PHY_ADVERTISE_1000_FULL;
420 hw->mac.link_speed = SPEED_1000;
421 hw->mac.link_duplex = DUPLEX_FULL;
422 break;
423 default:
424 BUG();
425 }
426}
427
428/**
429 * pch_gbe_check_options - Range Checking for Command Line Parameters
430 * @adapter: Board private structure
431 */
432void pch_gbe_check_options(struct pch_gbe_adapter *adapter)
433{
434 struct pch_gbe_hw *hw = &adapter->hw;
435 struct net_device *dev = adapter->netdev;
436 int val;
437
438 { /* Transmit Descriptor Count */
439 static const struct pch_gbe_option opt = {
440 .type = range_option,
441 .name = "Transmit Descriptors",
442 .err = "using default of "
443 __MODULE_STRING(PCH_GBE_DEFAULT_TXD),
444 .def = PCH_GBE_DEFAULT_TXD,
445 .arg = { .r = { .min = PCH_GBE_MIN_TXD,
446 .max = PCH_GBE_MAX_TXD } }
447 };
448 struct pch_gbe_tx_ring *tx_ring = adapter->tx_ring;
449 tx_ring->count = TxDescriptors;
450 pch_gbe_validate_option(value: &tx_ring->count, opt: &opt, adapter);
451 tx_ring->count = roundup(tx_ring->count,
452 PCH_GBE_TX_DESC_MULTIPLE);
453 }
454 { /* Receive Descriptor Count */
455 static const struct pch_gbe_option opt = {
456 .type = range_option,
457 .name = "Receive Descriptors",
458 .err = "using default of "
459 __MODULE_STRING(PCH_GBE_DEFAULT_RXD),
460 .def = PCH_GBE_DEFAULT_RXD,
461 .arg = { .r = { .min = PCH_GBE_MIN_RXD,
462 .max = PCH_GBE_MAX_RXD } }
463 };
464 struct pch_gbe_rx_ring *rx_ring = adapter->rx_ring;
465 rx_ring->count = RxDescriptors;
466 pch_gbe_validate_option(value: &rx_ring->count, opt: &opt, adapter);
467 rx_ring->count = roundup(rx_ring->count,
468 PCH_GBE_RX_DESC_MULTIPLE);
469 }
470 { /* Checksum Offload Enable/Disable */
471 static const struct pch_gbe_option opt = {
472 .type = enable_option,
473 .name = "Checksum Offload",
474 .err = "defaulting to Enabled",
475 .def = PCH_GBE_DEFAULT_RX_CSUM
476 };
477 val = XsumRX;
478 pch_gbe_validate_option(value: &val, opt: &opt, adapter);
479 if (!val)
480 dev->features &= ~NETIF_F_RXCSUM;
481 }
482 { /* Checksum Offload Enable/Disable */
483 static const struct pch_gbe_option opt = {
484 .type = enable_option,
485 .name = "Checksum Offload",
486 .err = "defaulting to Enabled",
487 .def = PCH_GBE_DEFAULT_TX_CSUM
488 };
489 val = XsumTX;
490 pch_gbe_validate_option(value: &val, opt: &opt, adapter);
491 if (!val)
492 dev->features &= ~NETIF_F_CSUM_MASK;
493 }
494 { /* Flow Control */
495 static const struct pch_gbe_option opt = {
496 .type = list_option,
497 .name = "Flow Control",
498 .err = "reading default settings from EEPROM",
499 .def = PCH_GBE_FC_DEFAULT,
500 .arg = { .l = { .nr = (int)ARRAY_SIZE(fc_list),
501 .p = fc_list } }
502 };
503 int tmp = FlowControl;
504
505 pch_gbe_validate_option(value: &tmp, opt: &opt, adapter);
506 hw->mac.fc = tmp;
507 }
508
509 pch_gbe_check_copper_options(adapter);
510}
511

source code of linux/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c