ksz9897_driver.c
Go to the documentation of this file.
1 /**
2  * @file ksz9897_driver.c
3  * @brief KSZ9897 7-port Gigabit Ethernet switch driver
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL NIC_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "core/ethernet_misc.h"
38 #include "debug.h"
39 
40 
41 /**
42  * @brief KSZ9897 Ethernet switch driver
43  **/
44 
46 {
70 };
71 
72 
73 /**
74  * @brief Tail tag rules (host to KSZ9897)
75  **/
76 
77 const uint16_t ksz9897IngressTailTag[8] =
78 {
85  HTONS(0),
87 };
88 
89 
90 /**
91  * @brief KSZ9897 Ethernet switch initialization
92  * @param[in] interface Underlying network interface
93  * @return Error code
94  **/
95 
97 {
98  uint_t port;
99  uint8_t temp;
100 
101  //Debug message
102  TRACE_INFO("Initializing KSZ9897...\r\n");
103 
104  //SPI slave mode?
105  if(interface->spiDriver != NULL)
106  {
107  //Initialize SPI interface
108  interface->spiDriver->init();
109 
110  //Wait for the serial interface to be ready
111  do
112  {
113  //Read CHIP_ID1 register
114  temp = ksz9897ReadSwitchReg8(interface, KSZ9897_CHIP_ID1);
115 
116  //The returned data is invalid until the serial interface is ready
117  } while(temp != KSZ9897_CHIP_ID1_DEFAULT);
118 
119 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
120  //Enable tail tag feature
124 
125  //Disable frame length check (silicon errata workaround 13)
129 #else
130  //Disable tail tag feature
134 
135  //Enable frame length check
139 #endif
140 
141  //Loop through the ports
143  {
144  //Skip host port
145  if(port != KSZ9897_PORT6)
146  {
147 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
148  //Port separation mode?
149  if(interface->port != 0)
150  {
151  //Disable packet transmission and address learning
152  ksz9897SetPortState(interface, port,
154  }
155  else
156 #endif
157  {
158  //Enable transmission, reception and address learning
159  ksz9897SetPortState(interface, port,
161  }
162  }
163  }
164 
165  //Restore default age count
169 
170  //Restore default age period
173 
174  //Add internal delay to ingress and egress RGMII clocks
179 
180  //Start switch operation
183  }
184  else if(interface->smiDriver != NULL)
185  {
186  //Initialize serial management interface
187  interface->smiDriver->init();
188  }
189  else
190  {
191  //Just for sanity
192  }
193 
194  //Loop through the ports
196  {
197  //Improve PHY receive performance (silicon errata workaround 1)
198  ksz9897WriteMmdReg(interface, port, 0x01, 0x6F, 0xDD0B);
199  ksz9897WriteMmdReg(interface, port, 0x01, 0x8F, 0x6032);
200  ksz9897WriteMmdReg(interface, port, 0x01, 0x9D, 0x248C);
201  ksz9897WriteMmdReg(interface, port, 0x01, 0x75, 0x0060);
202  ksz9897WriteMmdReg(interface, port, 0x01, 0xD3, 0x7777);
203  ksz9897WriteMmdReg(interface, port, 0x1C, 0x06, 0x3008);
204  ksz9897WriteMmdReg(interface, port, 0x1C, 0x08, 0x2001);
205 
206  //Improve transmit waveform amplitude (silicon errata workaround 2)
207  ksz9897WriteMmdReg(interface, port, 0x1C, 0x04, 0x00D0);
208 
209  //EEE must be manually disabled (silicon errata workaround 4)
211 
212  //Adjust power supply settings (silicon errata workaround 7)
213  ksz9897WriteMmdReg(interface, port, 0x1C, 0x13, 0x6EFF);
214  ksz9897WriteMmdReg(interface, port, 0x1C, 0x14, 0xE6FF);
215  ksz9897WriteMmdReg(interface, port, 0x1C, 0x15, 0x6EFF);
216  ksz9897WriteMmdReg(interface, port, 0x1C, 0x16, 0xE6FF);
217  ksz9897WriteMmdReg(interface, port, 0x1C, 0x17, 0x00FF);
218  ksz9897WriteMmdReg(interface, port, 0x1C, 0x18, 0x43FF);
219  ksz9897WriteMmdReg(interface, port, 0x1C, 0x19, 0xC3FF);
220  ksz9897WriteMmdReg(interface, port, 0x1C, 0x1A, 0x6FFF);
221  ksz9897WriteMmdReg(interface, port, 0x1C, 0x1B, 0x07FF);
222  ksz9897WriteMmdReg(interface, port, 0x1C, 0x1C, 0x0FFF);
223  ksz9897WriteMmdReg(interface, port, 0x1C, 0x1D, 0xE7FF);
224  ksz9897WriteMmdReg(interface, port, 0x1C, 0x1E, 0xEFFF);
225  ksz9897WriteMmdReg(interface, port, 0x1C, 0x20, 0xEEEE);
226 
227  //Select tri-color dual-LED mode (silicon errata workaround 15)
231 
232  //Debug message
233  TRACE_DEBUG("Port %u:\r\n", port);
234  //Dump PHY registers for debugging purpose
235  ksz9897DumpPhyReg(interface, port);
236  }
237 
238  //Perform custom configuration
239  ksz9897InitHook(interface);
240 
241  //Force the TCP/IP stack to poll the link state at startup
242  interface->phyEvent = TRUE;
243  //Notify the TCP/IP stack of the event
244  osSetEvent(&interface->netContext->event);
245 
246  //Successful initialization
247  return NO_ERROR;
248 }
249 
250 
251 /**
252  * @brief KSZ9897 custom configuration
253  * @param[in] interface Underlying network interface
254  **/
255 
256 __weak_func void ksz9897InitHook(NetInterface *interface)
257 {
258 }
259 
260 
261 /**
262  * @brief KSZ9897 timer handler
263  * @param[in] interface Underlying network interface
264  **/
265 
266 __weak_func void ksz9897Tick(NetInterface *interface)
267 {
268  uint_t port;
269  bool_t linkState;
270 
271 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
272  //Port separation mode?
273  if(interface->port != 0)
274  {
275  uint_t i;
276  NetContext *context;
277  NetInterface *virtualInterface;
278 
279  //Point to the TCP/IP stack context
280  context = interface->netContext;
281 
282  //Loop through network interfaces
283  for(i = 0; i < context->numInterfaces; i++)
284  {
285  //Point to the current interface
286  virtualInterface = &context->interfaces[i];
287 
288  //Check whether the current virtual interface is attached to the
289  //physical interface
290  if(virtualInterface == interface ||
291  virtualInterface->parent == interface)
292  {
293  //Retrieve current link state
294  linkState = ksz9897GetLinkState(interface, virtualInterface->port);
295 
296  //Link up or link down event?
297  if(linkState != virtualInterface->linkState)
298  {
299  //Set event flag
300  interface->phyEvent = TRUE;
301  //Notify the TCP/IP stack of the event
302  osSetEvent(&interface->netContext->event);
303  }
304  }
305  }
306  }
307  else
308 #endif
309  {
310  //Initialize link state
311  linkState = FALSE;
312 
313  //Loop through the ports
315  {
316  //Skip host port
317  if(port != KSZ9897_PORT6)
318  {
319  //Retrieve current link state
320  if(ksz9897GetLinkState(interface, port))
321  {
322  linkState = TRUE;
323  }
324  }
325  }
326 
327  //Link up or link down event?
328  if(linkState != interface->linkState)
329  {
330  //Set event flag
331  interface->phyEvent = TRUE;
332  //Notify the TCP/IP stack of the event
333  osSetEvent(&interface->netContext->event);
334  }
335  }
336 }
337 
338 
339 /**
340  * @brief Enable interrupts
341  * @param[in] interface Underlying network interface
342  **/
343 
345 {
346 }
347 
348 
349 /**
350  * @brief Disable interrupts
351  * @param[in] interface Underlying network interface
352  **/
353 
355 {
356 }
357 
358 
359 /**
360  * @brief KSZ9897 event handler
361  * @param[in] interface Underlying network interface
362  **/
363 
364 __weak_func void ksz9897EventHandler(NetInterface *interface)
365 {
366  uint_t port;
367  bool_t linkState;
368 
369 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
370  //Port separation mode?
371  if(interface->port != 0)
372  {
373  uint_t i;
374  NetContext *context;
375  NetInterface *virtualInterface;
376 
377  //Point to the TCP/IP stack context
378  context = interface->netContext;
379 
380  //Loop through network interfaces
381  for(i = 0; i < context->numInterfaces; i++)
382  {
383  //Point to the current interface
384  virtualInterface = &context->interfaces[i];
385 
386  //Check whether the current virtual interface is attached to the
387  //physical interface
388  if(virtualInterface == interface ||
389  virtualInterface->parent == interface)
390  {
391  //Get the port number associated with the current interface
392  port = virtualInterface->port;
393 
394  //Valid port?
395  if((port >= KSZ9897_PORT1 && port <= KSZ9897_PORT5) ||
396  port == KSZ9897_PORT7)
397  {
398  //Retrieve current link state
399  linkState = ksz9897GetLinkState(interface, port);
400 
401  //Link up event?
402  if(linkState && !virtualInterface->linkState)
403  {
404  //Retrieve host interface speed
405  interface->linkSpeed = ksz9897GetLinkSpeed(interface,
406  KSZ9897_PORT6);
407 
408  //Retrieve host interface duplex mode
409  interface->duplexMode = ksz9897GetDuplexMode(interface,
410  KSZ9897_PORT6);
411 
412  //Adjust MAC configuration parameters for proper operation
413  interface->nicDriver->updateMacConfig(interface);
414 
415  //Check current speed
416  virtualInterface->linkSpeed = ksz9897GetLinkSpeed(interface,
417  port);
418 
419  //Check current duplex mode
420  virtualInterface->duplexMode = ksz9897GetDuplexMode(interface,
421  port);
422 
423  //Update link state
424  virtualInterface->linkState = TRUE;
425 
426  //Process link state change event
427  nicNotifyLinkChange(virtualInterface);
428  }
429  //Link down event
430  else if(!linkState && virtualInterface->linkState)
431  {
432  //Update link state
433  virtualInterface->linkState = FALSE;
434 
435  //Process link state change event
436  nicNotifyLinkChange(virtualInterface);
437  }
438  }
439  }
440  }
441  }
442  else
443 #endif
444  {
445  //Initialize link state
446  linkState = FALSE;
447 
448  //Loop through the ports
450  {
451  //Skip host port
452  if(port != KSZ9897_PORT6)
453  {
454  //Retrieve current link state
455  if(ksz9897GetLinkState(interface, port))
456  {
457  linkState = TRUE;
458  }
459  }
460  }
461 
462  //Link up event?
463  if(linkState)
464  {
465  //Retrieve host interface speed
466  interface->linkSpeed = ksz9897GetLinkSpeed(interface, KSZ9897_PORT6);
467  //Retrieve host interface duplex mode
468  interface->duplexMode = ksz9897GetDuplexMode(interface, KSZ9897_PORT6);
469 
470  //Adjust MAC configuration parameters for proper operation
471  interface->nicDriver->updateMacConfig(interface);
472 
473  //Update link state
474  interface->linkState = TRUE;
475  }
476  else
477  {
478  //Update link state
479  interface->linkState = FALSE;
480  }
481 
482  //Process link state change event
483  nicNotifyLinkChange(interface);
484  }
485 }
486 
487 
488 /**
489  * @brief Add tail tag to Ethernet frame
490  * @param[in] interface Underlying network interface
491  * @param[in] buffer Multi-part buffer containing the payload
492  * @param[in,out] offset Offset to the first payload byte
493  * @param[in] ancillary Additional options passed to the stack along with
494  * the packet
495  * @return Error code
496  **/
497 
499  size_t *offset, NetTxAncillary *ancillary)
500 {
501  error_t error;
502 
503  //Initialize status code
504  error = NO_ERROR;
505 
506 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
507  //SPI slave mode?
508  if(interface->spiDriver != NULL)
509  {
510  //Valid port?
511  if(ancillary->port <= KSZ9897_PORT7)
512  {
513  size_t length;
514  const uint16_t *tailTag;
515 
516  //The two-byte tail tagging is used to indicate the destination port
517  tailTag = &ksz9897IngressTailTag[ancillary->port];
518 
519  //Retrieve the length of the Ethernet frame
520  length = netBufferGetLength(buffer) - *offset;
521 
522  //The host controller should manually add padding to the packet before
523  //inserting the tail tag
524  error = ethPadFrame(buffer, &length);
525 
526  //Check status code
527  if(!error)
528  {
529  //The tail tag is inserted at the end of the packet, just before
530  //the CRC
531  error = netBufferAppend(buffer, tailTag, sizeof(uint16_t));
532  }
533  }
534  else
535  {
536  //The port number is not valid
537  error = ERROR_INVALID_PORT;
538  }
539  }
540 #endif
541 
542  //Return status code
543  return error;
544 }
545 
546 
547 /**
548  * @brief Decode tail tag from incoming Ethernet frame
549  * @param[in] interface Underlying network interface
550  * @param[in,out] frame Pointer to the received Ethernet frame
551  * @param[in,out] length Length of the frame, in bytes
552  * @param[in,out] ancillary Additional options passed to the stack along with
553  * the packet
554  * @return Error code
555  **/
556 
557 error_t ksz9897UntagFrame(NetInterface *interface, uint8_t **frame,
558  size_t *length, NetRxAncillary *ancillary)
559 {
560  error_t error;
561 
562  //Initialize status code
563  error = NO_ERROR;
564 
565 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
566  //SPI slave mode?
567  if(interface->spiDriver != NULL)
568  {
569  //Valid Ethernet frame received?
570  if(*length >= (sizeof(EthHeader) + sizeof(uint8_t)))
571  {
572  uint8_t *tailTag;
573 
574  //The tail tag is inserted at the end of the packet, just before
575  //the CRC
576  tailTag = *frame + *length - sizeof(uint8_t);
577 
578  //The one byte tail tagging is used to indicate the source port
579  ancillary->port = (*tailTag & KSZ9897_TAIL_TAG_SRC_PORT) + 1;
580 
581  //Strip tail tag from Ethernet frame
582  *length -= sizeof(uint8_t);
583  }
584  else
585  {
586  //Drop the received frame
587  error = ERROR_INVALID_LENGTH;
588  }
589  }
590  else
591  {
592  //Tail tagging mode cannot be enabled through MDC/MDIO interface
593  ancillary->port = 0;
594  }
595 #endif
596 
597  //Return status code
598  return error;
599 }
600 
601 
602 /**
603  * @brief Get link state
604  * @param[in] interface Underlying network interface
605  * @param[in] port Port number
606  * @return Link state
607  **/
608 
610 {
611  uint16_t value;
612  bool_t linkState;
613 
614  //Check port number
615  if(port >= KSZ9897_PORT1 && port <= KSZ9897_PORT5)
616  {
617  //Any link failure condition is latched in the BMSR register. Reading
618  //the register twice will always return the actual link status
619  value = ksz9897ReadPhyReg(interface, port, KSZ9897_BMSR);
620  value = ksz9897ReadPhyReg(interface, port, KSZ9897_BMSR);
621 
622  //Retrieve current link state
623  linkState = (value & KSZ9897_BMSR_LINK_STATUS) ? TRUE : FALSE;
624  }
625  else if(port == KSZ9897_PORT7)
626  {
627  //An external PHY can optionally be connected to port 7
628  linkState = ksz9897GetPort7LinkState(interface);
629  }
630  else
631  {
632  //The specified port number is not valid
633  linkState = FALSE;
634  }
635 
636  //Return link status
637  return linkState;
638 }
639 
640 
641 /**
642  * @brief Get link speed
643  * @param[in] interface Underlying network interface
644  * @param[in] port Port number
645  * @return Link speed
646  **/
647 
648 uint32_t ksz9897GetLinkSpeed(NetInterface *interface, uint8_t port)
649 {
650  uint8_t type;
651  uint16_t value;
652  uint32_t linkSpeed;
653 
654  //Check port number
655  if(port >= KSZ9897_PORT1 && port <= KSZ9897_PORT5)
656  {
657  //Read PHY control register
659 
660  //Retrieve current link speed
662  {
663  //1000BASE-T
664  linkSpeed = NIC_LINK_SPEED_1GBPS;
665  }
666  else if((value & KSZ9897_PHYCON_SPEED_100BTX) != 0)
667  {
668  //100BASE-TX
669  linkSpeed = NIC_LINK_SPEED_100MBPS;
670  }
671  else if((value & KSZ9897_PHYCON_SPEED_10BT) != 0)
672  {
673  //10BASE-T
674  linkSpeed = NIC_LINK_SPEED_10MBPS;
675  }
676  else
677  {
678  //The link speed is not valid
679  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
680  }
681  }
682  else if(port == KSZ9897_PORT6)
683  {
684  //SPI slave mode?
685  if(interface->spiDriver != NULL)
686  {
687  //Read port 6 XMII control 1 register
689 
690  //Retrieve host interface type
692 
693  //Gigabit interface?
696  {
697  //1000 Mb/s mode
698  linkSpeed = NIC_LINK_SPEED_1GBPS;
699  }
700  else
701  {
702  //Read port 6 XMII control 0 register
704 
705  //Retrieve host interface speed
707  {
708  //100 Mb/s mode
709  linkSpeed = NIC_LINK_SPEED_100MBPS;
710  }
711  else
712  {
713  //10 Mb/s mode
714  linkSpeed = NIC_LINK_SPEED_10MBPS;
715  }
716  }
717  }
718  else
719  {
720  //The MDC/MDIO interface does not have access to all the configuration
721  //registers. It can only access the standard MIIM registers
722  linkSpeed = NIC_LINK_SPEED_100MBPS;
723  }
724  }
725  else if(port == KSZ9897_PORT7)
726  {
727  //An external PHY can optionally be connected to port 7
728  linkSpeed = ksz9897GetPort7LinkSpeed(interface);
729  }
730  else
731  {
732  //The specified port number is not valid
733  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
734  }
735 
736  //Return link speed
737  return linkSpeed;
738 }
739 
740 
741 /**
742  * @brief Get duplex mode
743  * @param[in] interface Underlying network interface
744  * @param[in] port Port number
745  * @return Duplex mode
746  **/
747 
749 {
750  uint16_t value;
751  NicDuplexMode duplexMode;
752 
753  //Check port number
754  if(port >= KSZ9897_PORT1 && port <= KSZ9897_PORT5)
755  {
756  //Read PHY control register
758 
759  //Retrieve current duplex mode
761  {
762  duplexMode = NIC_FULL_DUPLEX_MODE;
763  }
764  else
765  {
766  duplexMode = NIC_HALF_DUPLEX_MODE;
767  }
768  }
769  else if(port == KSZ9897_PORT6)
770  {
771  //SPI slave mode?
772  if(interface->spiDriver != NULL)
773  {
774  //Read port 6 XMII control 0 register
776 
777  //Retrieve host interface duplex mode
779  {
780  duplexMode = NIC_FULL_DUPLEX_MODE;
781  }
782  else
783  {
784  duplexMode = NIC_HALF_DUPLEX_MODE;
785  }
786  }
787  else
788  {
789  //The MDC/MDIO interface does not have access to all the configuration
790  //registers. It can only access the standard MIIM registers
791  duplexMode = NIC_FULL_DUPLEX_MODE;
792  }
793  }
794  else if(port == KSZ9897_PORT7)
795  {
796  //An external PHY can optionally be connected to port 7
797  duplexMode = ksz9897GetPort7DuplexMode(interface);
798  }
799  else
800  {
801  //The specified port number is not valid
802  duplexMode = NIC_UNKNOWN_DUPLEX_MODE;
803  }
804 
805  //Return duplex mode
806  return duplexMode;
807 }
808 
809 
810 /**
811  * @brief Get port 7 link state
812  * @param[in] interface Underlying network interface
813  * @return Link state
814  **/
815 
817 {
818  //Return current link status
819  return FALSE;
820 }
821 
822 
823 /**
824  * @brief Get port 7 link speed
825  * @param[in] interface Underlying network interface
826  * @return Link speed
827  **/
828 
829 __weak_func uint32_t ksz9897GetPort7LinkSpeed(NetInterface *interface)
830 {
831  //Return current link speed
832  return NIC_LINK_SPEED_UNKNOWN;
833 }
834 
835 
836 /**
837  * @brief Get port 7 duplex mode
838  * @param[in] interface Underlying network interface
839  * @return Duplex mode
840  **/
841 
843 {
844  //Return current duplex mode
846 }
847 
848 
849 /**
850  * @brief Set port state
851  * @param[in] interface Underlying network interface
852  * @param[in] port Port number
853  * @param[in] state Port state
854  **/
855 
856 void ksz9897SetPortState(NetInterface *interface, uint8_t port,
857  SwitchPortState state)
858 {
859  uint8_t temp;
860 
861  //Check port number
862  if((port >= KSZ9897_PORT1 && port <= KSZ9897_PORT5) ||
863  port == KSZ9897_PORT7)
864  {
865  //Read MSTP state register
867 
868  //Update port state
869  switch(state)
870  {
871  //Listening state
876  break;
877 
878  //Learning state
883  break;
884 
885  //Forwarding state
890  break;
891 
892  //Disabled state
893  default:
897  break;
898  }
899 
900  //Write the value back to MSTP state register
902  }
903 }
904 
905 
906 /**
907  * @brief Get port state
908  * @param[in] interface Underlying network interface
909  * @param[in] port Port number
910  * @return Port state
911  **/
912 
914 {
915  uint8_t temp;
916  SwitchPortState state;
917 
918  //Check port number
919  if((port >= KSZ9897_PORT1 && port <= KSZ9897_PORT5) ||
920  port == KSZ9897_PORT7)
921  {
922  //Read MSTP state register
924 
925  //Check port state
926  if((temp & KSZ9897_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
927  (temp & KSZ9897_PORTn_MSTP_STATE_RECEIVE_EN) == 0 &&
929  {
930  //Disabled state
932  }
933  else if((temp & KSZ9897_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
934  (temp & KSZ9897_PORTn_MSTP_STATE_RECEIVE_EN) != 0 &&
936  {
937  //Listening state
939  }
940  else if((temp & KSZ9897_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
941  (temp & KSZ9897_PORTn_MSTP_STATE_RECEIVE_EN) == 0 &&
943  {
944  //Learning state
946  }
947  else if((temp & KSZ9897_PORTn_MSTP_STATE_TRANSMIT_EN) != 0 &&
948  (temp & KSZ9897_PORTn_MSTP_STATE_RECEIVE_EN) != 0 &&
950  {
951  //Forwarding state
953  }
954  else
955  {
956  //Unknown state
958  }
959  }
960  else
961  {
962  //The specified port number is not valid
964  }
965 
966  //Return port state
967  return state;
968 }
969 
970 
971 /**
972  * @brief Set aging time for dynamic filtering entries
973  * @param[in] interface Underlying network interface
974  * @param[in] agingTime Aging time, in seconds
975  **/
976 
977 void ksz9897SetAgingTime(NetInterface *interface, uint32_t agingTime)
978 {
979  //The Age Period in combination with the Age Count field determines the
980  //aging time of dynamic entries in the address lookup table
981  agingTime = (agingTime + 3) / 4;
982 
983  //Limit the range of the parameter
984  agingTime = MIN(agingTime, 255);
985 
986  //Write the value to Switch Lookup Engine Control 3 register
988  (uint8_t) agingTime);
989 }
990 
991 
992 /**
993  * @brief Enable IGMP snooping
994  * @param[in] interface Underlying network interface
995  * @param[in] enable Enable or disable IGMP snooping
996  **/
997 
999 {
1000  uint8_t temp;
1001 
1002  //Read the Global Port Mirroring and Snooping Control register
1003  temp = ksz9897ReadSwitchReg8(interface,
1005 
1006  //Enable or disable IGMP snooping
1007  if(enable)
1008  {
1010  }
1011  else
1012  {
1014  }
1015 
1016  //Write the value back to Global Port Mirroring and Snooping Control register
1018  temp);
1019 }
1020 
1021 
1022 /**
1023  * @brief Enable MLD snooping
1024  * @param[in] interface Underlying network interface
1025  * @param[in] enable Enable or disable MLD snooping
1026  **/
1027 
1029 {
1030  uint8_t temp;
1031 
1032  //Read the Global Port Mirroring and Snooping Control register
1033  temp = ksz9897ReadSwitchReg8(interface,
1035 
1036  //Enable or disable MLD snooping
1037  if(enable)
1038  {
1040  }
1041  else
1042  {
1044  }
1045 
1046  //Write the value back to Global Port Mirroring and Snooping Control register
1048  temp);
1049 }
1050 
1051 
1052 /**
1053  * @brief Enable reserved multicast table
1054  * @param[in] interface Underlying network interface
1055  * @param[in] enable Enable or disable reserved group addresses
1056  **/
1057 
1059 {
1060  uint8_t temp;
1061 
1062  //Read the Switch Lookup Engine Control 0 register
1064 
1065  //Enable or disable the reserved multicast table
1066  if(enable)
1067  {
1069  }
1070  else
1071  {
1073  }
1074 
1075  //Write the value back to Switch Lookup Engine Control 0 register
1077 }
1078 
1079 
1080 /**
1081  * @brief Add a new entry to the static MAC table
1082  * @param[in] interface Underlying network interface
1083  * @param[in] entry Pointer to the forwarding database entry
1084  * @return Error code
1085  **/
1086 
1088  const SwitchFdbEntry *entry)
1089 {
1090  error_t error;
1091  uint_t i;
1092  uint_t j;
1093  uint32_t value;
1094  SwitchFdbEntry currentEntry;
1095 
1096  //Keep track of the first free entry
1098 
1099  //Loop through the static MAC table
1100  for(i = 0; i < KSZ9897_STATIC_MAC_TABLE_SIZE; i++)
1101  {
1102  //Read current entry
1103  error = ksz9897GetStaticFdbEntry(interface, i, &currentEntry);
1104 
1105  //Valid entry?
1106  if(!error)
1107  {
1108  //Check whether the table already contains the specified MAC address
1109  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
1110  {
1111  j = i;
1112  break;
1113  }
1114  }
1115  else
1116  {
1117  //Keep track of the first free entry
1119  {
1120  j = i;
1121  }
1122  }
1123  }
1124 
1125  //Any entry available?
1127  {
1128  //Write the Static Address Table Entry 1 register
1131 
1132  //Set the relevant forward ports
1133  if(entry->destPorts == SWITCH_CPU_PORT_MASK)
1134  {
1136  }
1137  else
1138  {
1139  value = entry->destPorts & KSZ9897_PORT_MASK;
1140  }
1141 
1142  //Enable overriding of port state
1143  if(entry->override)
1144  {
1146  }
1147 
1148  //Write the Static Address Table Entry 2 register
1150 
1151  //Copy MAC address (first 16 bits)
1152  value = (entry->macAddr.b[0] << 8) | entry->macAddr.b[1];
1153 
1154  //Write the Static Address Table Entry 3 register
1156 
1157  //Copy MAC address (last 32 bits)
1158  value = (entry->macAddr.b[2] << 24) | (entry->macAddr.b[3] << 16) |
1159  (entry->macAddr.b[4] << 8) | entry->macAddr.b[5];
1160 
1161  //Write the Static Address Table Entry 4 register
1163 
1164  //Write the TABLE_INDEX field with the 4-bit index value
1166  //Set the TABLE_SELECT bit to 0 to select the static address table
1168  //Set the ACTION bit to 0 to indicate a write operation
1170  //Set the START_FINISH bit to 1 to initiate the operation
1172 
1173  //Start the write operation
1175  value);
1176 
1177  //When the operation is complete, the START_FINISH bit will be cleared
1178  //automatically
1179  do
1180  {
1181  //Read the Static Address and Reserved Multicast Table Control register
1182  value = ksz9897ReadSwitchReg32(interface,
1184 
1185  //Poll the START_FINISH bit
1187 
1188  //Successful processing
1189  error = NO_ERROR;
1190  }
1191  else
1192  {
1193  //The static MAC table is full
1194  error = ERROR_TABLE_FULL;
1195  }
1196 
1197  //Return status code
1198  return error;
1199 }
1200 
1201 
1202 /**
1203  * @brief Remove an entry from the static MAC table
1204  * @param[in] interface Underlying network interface
1205  * @param[in] entry Forwarding database entry to remove from the table
1206  * @return Error code
1207  **/
1208 
1210  const SwitchFdbEntry *entry)
1211 {
1212  error_t error;
1213  uint_t j;
1214  uint32_t value;
1215  SwitchFdbEntry currentEntry;
1216 
1217  //Loop through the static MAC table
1218  for(j = 0; j < KSZ9897_STATIC_MAC_TABLE_SIZE; j++)
1219  {
1220  //Read current entry
1221  error = ksz9897GetStaticFdbEntry(interface, j, &currentEntry);
1222 
1223  //Valid entry?
1224  if(!error)
1225  {
1226  //Check whether the table contains the specified MAC address
1227  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
1228  {
1229  break;
1230  }
1231  }
1232  }
1233 
1234  //Any matching entry?
1236  {
1237  //Clear Static Address Table Entry registers
1242 
1243  //Write the TABLE_INDEX field with the 4-bit index value
1245  //Set the TABLE_SELECT bit to 0 to select the static address table
1247  //Set the ACTION bit to 0 to indicate a write operation
1249  //Set the START_FINISH bit to 1 to initiate the operation
1251 
1252  //Start the write operation
1254  value);
1255 
1256  //When the operation is complete, the START_FINISH bit will be cleared
1257  //automatically
1258  do
1259  {
1260  //Read the Static Address and Reserved Multicast Table Control register
1261  value = ksz9897ReadSwitchReg32(interface,
1263 
1264  //Poll the START_FINISH bit
1266 
1267  //Successful processing
1268  error = NO_ERROR;
1269  }
1270  else
1271  {
1272  //The static MAC table does not contain the specified address
1273  error = ERROR_NOT_FOUND;
1274  }
1275 
1276  //Return status code
1277  return error;
1278 }
1279 
1280 
1281 /**
1282  * @brief Read an entry from the static MAC table
1283  * @param[in] interface Underlying network interface
1284  * @param[in] index Zero-based index of the entry to read
1285  * @param[out] entry Pointer to the forwarding database entry
1286  * @return Error code
1287  **/
1288 
1290  SwitchFdbEntry *entry)
1291 {
1292  error_t error;
1293  uint32_t value;
1294 
1295  //Check index parameter
1296  if(index < KSZ9897_STATIC_MAC_TABLE_SIZE)
1297  {
1298  //Write the TABLE_INDEX field with the 4-bit index value
1300  //Set the TABLE_SELECT bit to 0 to select the static address table
1302  //Set the ACTION bit to 1 to indicate a read operation
1304  //Set the START_FINISH bit to 1 to initiate the operation
1306 
1307  //Start the read operation
1309  value);
1310 
1311  //When the operation is complete, the START_FINISH bit will be cleared
1312  //automatically
1313  do
1314  {
1315  //Read the Static Address and Reserved Multicast Table Control register
1316  value = ksz9897ReadSwitchReg32(interface,
1318 
1319  //Poll the START_FINISH bit
1321 
1322  //Read the Static Address Table Entry 1 register
1324 
1325  //Valid entry?
1327  {
1328  //Read the Static Address Table Entry 2 register
1330 
1331  //Retrieve the ports associated with this MAC address
1332  entry->srcPort = 0;
1334 
1335  //Check the value of the OVERRIDE bit
1337  {
1338  entry->override = TRUE;
1339  }
1340  else
1341  {
1342  entry->override = FALSE;
1343  }
1344 
1345  //Read the Static Address Table Entry 3 register
1347 
1348  //Copy MAC address (first 16 bits)
1349  entry->macAddr.b[0] = (value >> 8) & 0xFF;
1350  entry->macAddr.b[1] = value & 0xFF;
1351 
1352  //Read the Static Address Table Entry 4 register
1354 
1355  //Copy MAC address (last 32 bits)
1356  entry->macAddr.b[2] = (value >> 24) & 0xFF;
1357  entry->macAddr.b[3] = (value >> 16) & 0xFF;
1358  entry->macAddr.b[4] = (value >> 8) & 0xFF;
1359  entry->macAddr.b[5] = value & 0xFF;
1360 
1361  //Successful processing
1362  error = NO_ERROR;
1363  }
1364  else
1365  {
1366  //The entry is not valid
1367  error = ERROR_INVALID_ENTRY;
1368  }
1369  }
1370  else
1371  {
1372  //The end of the table has been reached
1373  error = ERROR_END_OF_TABLE;
1374  }
1375 
1376  //Return status code
1377  return error;
1378 }
1379 
1380 
1381 /**
1382  * @brief Flush static MAC table
1383  * @param[in] interface Underlying network interface
1384  **/
1385 
1387 {
1388  uint_t i;
1389  uint32_t value;
1390 
1391  //Loop through the static MAC table
1392  for(i = 0; i < KSZ9897_STATIC_MAC_TABLE_SIZE; i++)
1393  {
1394  //Clear Static Address Table Entry registers
1399 
1400  //Write the TABLE_INDEX field with the 4-bit index value
1402  //Set the TABLE_SELECT bit to 0 to select the static address table
1404  //Set the ACTION bit to 0 to indicate a write operation
1406  //Set the START_FINISH bit to 1 to initiate the operation
1408 
1409  //Start the write operation
1411  value);
1412 
1413  //When the operation is complete, the START_FINISH bit will be cleared
1414  //automatically
1415  do
1416  {
1417  //Read the Static Address and Reserved Multicast Table Control register
1418  value = ksz9897ReadSwitchReg32(interface,
1420 
1421  //Poll the START_FINISH bit
1423  }
1424 }
1425 
1426 
1427 /**
1428  * @brief Read an entry from the dynamic MAC table
1429  * @param[in] interface Underlying network interface
1430  * @param[in] index Zero-based index of the entry to read
1431  * @param[out] entry Pointer to the forwarding database entry
1432  * @return Error code
1433  **/
1434 
1436  SwitchFdbEntry *entry)
1437 {
1438  error_t error;
1439  uint32_t value;
1440 
1441  //First entry?
1442  if(index == 0)
1443  {
1444  //Clear the ALU Table Access Control register to stop any operation
1446 
1447  //Start the search operation
1451  }
1452 
1453  //Poll the VALID_ENTRY_OR_SEARCH_END bit until it is set
1454  do
1455  {
1456  //Read the ALU Table Access Control register
1458 
1459  //This bit goes high to indicate either a new valid entry is returned or
1460  //the search is complete
1462 
1463  //Check whether the next valid entry is ready
1464  if((value & KSZ9897_ALU_TABLE_CTRL_VALID) != 0)
1465  {
1466  //Store the data from the ALU table entry
1467  entry->destPorts = 0;
1468  entry->override = FALSE;
1469 
1470  //Read the ALU Table Entry 1 and 2 registers
1473 
1474  //Retrieve the port associated with this MAC address
1476  {
1478  entry->srcPort = KSZ9897_PORT1;
1479  break;
1481  entry->srcPort = KSZ9897_PORT2;
1482  break;
1484  entry->srcPort = KSZ9897_PORT3;
1485  break;
1487  entry->srcPort = KSZ9897_PORT4;
1488  break;
1490  entry->srcPort = KSZ9897_PORT5;
1491  break;
1493  entry->srcPort = KSZ9897_PORT6;
1494  break;
1496  entry->srcPort = KSZ9897_PORT7;
1497  break;
1498  default:
1499  entry->srcPort = 0;
1500  break;
1501  }
1502 
1503  //Read the ALU Table Entry 3 register
1505 
1506  //Copy MAC address (first 16 bits)
1507  entry->macAddr.b[0] = (value >> 8) & 0xFF;
1508  entry->macAddr.b[1] = value & 0xFF;
1509 
1510  //Read the ALU Table Entry 4 register
1512 
1513  //Copy MAC address (last 32 bits)
1514  entry->macAddr.b[2] = (value >> 24) & 0xFF;
1515  entry->macAddr.b[3] = (value >> 16) & 0xFF;
1516  entry->macAddr.b[4] = (value >> 8) & 0xFF;
1517  entry->macAddr.b[5] = value & 0xFF;
1518 
1519  //Successful processing
1520  error = NO_ERROR;
1521  }
1522  else
1523  {
1524  //The search can be stopped any time by setting the START_FINISH bit to 0
1526 
1527  //The end of the table has been reached
1528  error = ERROR_END_OF_TABLE;
1529  }
1530 
1531  //Return status code
1532  return error;
1533 }
1534 
1535 
1536 /**
1537  * @brief Flush dynamic MAC table
1538  * @param[in] interface Underlying network interface
1539  * @param[in] port Port number
1540  **/
1541 
1543 {
1544  uint_t temp;
1545  uint8_t state;
1546 
1547  //Flush only dynamic table entries
1552 
1553  //Valid port number?
1554  if(port >= KSZ9897_PORT1 && port <= KSZ9897_PORT7)
1555  {
1556  //Save the current state of the port
1558 
1559  //Turn off learning capability
1562 
1563  //All the entries associated with a port that has its learning capability
1564  //being turned off will be flushed
1568 
1569  //Restore the original state of the port
1571  }
1572  else
1573  {
1574  //Trigger a flush of the entire address lookup table
1578  }
1579 }
1580 
1581 
1582 /**
1583  * @brief Set forward ports for unknown multicast packets
1584  * @param[in] interface Underlying network interface
1585  * @param[in] enable Enable or disable forwarding of unknown multicast packets
1586  * @param[in] forwardPorts Port map
1587  **/
1588 
1590  bool_t enable, uint32_t forwardPorts)
1591 {
1592  uint32_t temp;
1593 
1594  //Read Unknown Multicast Control register
1596 
1597  //Clear port map
1599 
1600  //Enable or disable forwarding of unknown multicast packets
1601  if(enable)
1602  {
1603  //Enable forwarding
1605 
1606  //Check whether unknown multicast packets should be forwarded to the CPU port
1607  if((forwardPorts & SWITCH_CPU_PORT_MASK) != 0)
1608  {
1610  }
1611 
1612  //Select the desired forward ports
1613  temp |= forwardPorts & KSZ9897_UNKONWN_MULTICAST_CTRL_FWD_MAP_ALL;
1614  }
1615  else
1616  {
1617  //Disable forwarding
1619  }
1620 
1621  //Write the value back to Unknown Multicast Control register
1623 }
1624 
1625 
1626 /**
1627  * @brief Write PHY register
1628  * @param[in] interface Underlying network interface
1629  * @param[in] port Port number
1630  * @param[in] address PHY register address
1631  * @param[in] data Register value
1632  **/
1633 
1634 void ksz9897WritePhyReg(NetInterface *interface, uint8_t port,
1635  uint8_t address, uint16_t data)
1636 {
1637  uint16_t n;
1638 
1639  //SPI slave mode?
1640  if(interface->spiDriver != NULL)
1641  {
1642  //The SPI interface provides access to all PHY registers
1644  //Write the 16-bit value
1645  ksz9897WriteSwitchReg16(interface, n, data);
1646  }
1647  else if(interface->smiDriver != NULL)
1648  {
1649  //Write the specified PHY register
1650  interface->smiDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1651  }
1652  else
1653  {
1654  //Write the specified PHY register
1655  interface->nicDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1656  }
1657 }
1658 
1659 
1660 /**
1661  * @brief Read PHY register
1662  * @param[in] interface Underlying network interface
1663  * @param[in] port Port number
1664  * @param[in] address PHY register address
1665  * @return Register value
1666  **/
1667 
1668 uint16_t ksz9897ReadPhyReg(NetInterface *interface, uint8_t port,
1669  uint8_t address)
1670 {
1671  uint16_t n;
1672  uint16_t data;
1673 
1674  //SPI slave mode?
1675  if(interface->spiDriver != NULL)
1676  {
1677  //The SPI interface provides access to all PHY registers
1679  //Read the 16-bit value
1680  data = ksz9897ReadSwitchReg16(interface, n);
1681  }
1682  else if(interface->smiDriver != NULL)
1683  {
1684  //Read the specified PHY register
1685  data = interface->smiDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1686  }
1687  else
1688  {
1689  //Read the specified PHY register
1690  data = interface->nicDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1691  }
1692 
1693  //Return register value
1694  return data;
1695 }
1696 
1697 
1698 /**
1699  * @brief Dump PHY registers for debugging purpose
1700  * @param[in] interface Underlying network interface
1701  * @param[in] port Port number
1702  **/
1703 
1704 void ksz9897DumpPhyReg(NetInterface *interface, uint8_t port)
1705 {
1706  uint8_t i;
1707 
1708  //Loop through PHY registers
1709  for(i = 0; i < 32; i++)
1710  {
1711  //Display current PHY register
1712  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
1713  ksz9897ReadPhyReg(interface, port, i));
1714  }
1715 
1716  //Terminate with a line feed
1717  TRACE_DEBUG("\r\n");
1718 }
1719 
1720 
1721 /**
1722  * @brief Write MMD register
1723  * @param[in] interface Underlying network interface
1724  * @param[in] port Port number
1725  * @param[in] devAddr Device address
1726  * @param[in] regAddr Register address
1727  * @param[in] data Register value
1728  **/
1729 
1730 void ksz9897WriteMmdReg(NetInterface *interface, uint8_t port,
1731  uint8_t devAddr, uint16_t regAddr, uint16_t data)
1732 {
1733  //Select register operation
1736 
1737  //Write MMD register address
1739 
1740  //Select data operation
1743 
1744  //Write the content of the MMD register
1746 }
1747 
1748 
1749 /**
1750  * @brief Read MMD register
1751  * @param[in] interface Underlying network interface
1752  * @param[in] port Port number
1753  * @param[in] devAddr Device address
1754  * @param[in] regAddr Register address
1755  * @return Register value
1756  **/
1757 
1758 uint16_t ksz9897ReadMmdReg(NetInterface *interface, uint8_t port,
1759  uint8_t devAddr, uint16_t regAddr)
1760 {
1761  //Select register operation
1764 
1765  //Write MMD register address
1767 
1768  //Select data operation
1771 
1772  //Read the content of the MMD register
1773  return ksz9897ReadPhyReg(interface, port, KSZ9897_MMDAADR);
1774 }
1775 
1776 
1777 /**
1778  * @brief Write SGMII register
1779  * @param[in] interface Underlying network interface
1780  * @param[in] address SGMII register address
1781  * @param[in] data Register value
1782  **/
1783 
1784 void ksz9897WriteSgmiiReg(NetInterface *interface, uint32_t address,
1785  uint16_t data)
1786 {
1787  //Write the SGMII register address to the Port SGMII Address register
1789 
1790  //Write the SGMII register data to the Port SGMII Data register
1792 }
1793 
1794 
1795 /**
1796  * @brief Read SGMII register
1797  * @param[in] interface Underlying network interface
1798  * @param[in] address SGMII register address
1799  * @return Register value
1800  **/
1801 
1802 uint16_t ksz9897ReadSgmiiReg(NetInterface *interface, uint32_t address)
1803 {
1804  //Write the SGMII register address to the Port SGMII Address register
1806 
1807  //Read the SGMII register data from the Port SGMII Data register
1809 }
1810 
1811 
1812 /**
1813  * @brief Write switch register (8 bits)
1814  * @param[in] interface Underlying network interface
1815  * @param[in] address Switch register address
1816  * @param[in] data Register value
1817  **/
1818 
1819 void ksz9897WriteSwitchReg8(NetInterface *interface, uint16_t address,
1820  uint8_t data)
1821 {
1822  uint32_t command;
1823 
1824  //SPI slave mode?
1825  if(interface->spiDriver != NULL)
1826  {
1827  //Set up a write operation
1828  command = KSZ9897_SPI_CMD_WRITE;
1829  //Set register address
1830  command |= (address << 5) & KSZ9897_SPI_CMD_ADDR;
1831 
1832  //Pull the CS pin low
1833  interface->spiDriver->assertCs();
1834 
1835  //Write 32-bit command
1836  interface->spiDriver->transfer((command >> 24) & 0xFF);
1837  interface->spiDriver->transfer((command >> 16) & 0xFF);
1838  interface->spiDriver->transfer((command >> 8) & 0xFF);
1839  interface->spiDriver->transfer(command & 0xFF);
1840 
1841  //Write 8-bit data
1842  interface->spiDriver->transfer(data);
1843 
1844  //Terminate the operation by raising the CS pin
1845  interface->spiDriver->deassertCs();
1846  }
1847  else
1848  {
1849  //The MDC/MDIO interface does not have access to all the configuration
1850  //registers. It can only access the standard MIIM registers
1851  }
1852 }
1853 
1854 
1855 /**
1856  * @brief Read switch register (8 bits)
1857  * @param[in] interface Underlying network interface
1858  * @param[in] address Switch register address
1859  * @return Register value
1860  **/
1861 
1862 uint8_t ksz9897ReadSwitchReg8(NetInterface *interface, uint16_t address)
1863 {
1864  uint8_t data;
1865  uint32_t command;
1866 
1867  //SPI slave mode?
1868  if(interface->spiDriver != NULL)
1869  {
1870  //Set up a read operation
1871  command = KSZ9897_SPI_CMD_READ;
1872  //Set register address
1873  command |= (address << 5) & KSZ9897_SPI_CMD_ADDR;
1874 
1875  //Pull the CS pin low
1876  interface->spiDriver->assertCs();
1877 
1878  //Write 32-bit command
1879  interface->spiDriver->transfer((command >> 24) & 0xFF);
1880  interface->spiDriver->transfer((command >> 16) & 0xFF);
1881  interface->spiDriver->transfer((command >> 8) & 0xFF);
1882  interface->spiDriver->transfer(command & 0xFF);
1883 
1884  //Read 8-bit data
1885  data = interface->spiDriver->transfer(0xFF);
1886 
1887  //Terminate the operation by raising the CS pin
1888  interface->spiDriver->deassertCs();
1889  }
1890  else
1891  {
1892  //The MDC/MDIO interface does not have access to all the configuration
1893  //registers. It can only access the standard MIIM registers
1894  data = 0;
1895  }
1896 
1897  //Return register value
1898  return data;
1899 }
1900 
1901 
1902 /**
1903  * @brief Write switch register (16 bits)
1904  * @param[in] interface Underlying network interface
1905  * @param[in] address Switch register address
1906  * @param[in] data Register value
1907  **/
1908 
1910  uint16_t data)
1911 {
1912  uint32_t command;
1913 
1914  //SPI slave mode?
1915  if(interface->spiDriver != NULL)
1916  {
1917  //Set up a write operation
1918  command = KSZ9897_SPI_CMD_WRITE;
1919  //Set register address
1920  command |= (address << 5) & KSZ9897_SPI_CMD_ADDR;
1921 
1922  //Pull the CS pin low
1923  interface->spiDriver->assertCs();
1924 
1925  //Write 32-bit command
1926  interface->spiDriver->transfer((command >> 24) & 0xFF);
1927  interface->spiDriver->transfer((command >> 16) & 0xFF);
1928  interface->spiDriver->transfer((command >> 8) & 0xFF);
1929  interface->spiDriver->transfer(command & 0xFF);
1930 
1931  //Write 16-bit data
1932  interface->spiDriver->transfer((data >> 8) & 0xFF);
1933  interface->spiDriver->transfer(data & 0xFF);
1934 
1935  //Terminate the operation by raising the CS pin
1936  interface->spiDriver->deassertCs();
1937  }
1938  else
1939  {
1940  //The MDC/MDIO interface does not have access to all the configuration
1941  //registers. It can only access the standard MIIM registers
1942  }
1943 }
1944 
1945 
1946 /**
1947  * @brief Read switch register (16 bits)
1948  * @param[in] interface Underlying network interface
1949  * @param[in] address Switch register address
1950  * @return Register value
1951  **/
1952 
1953 uint16_t ksz9897ReadSwitchReg16(NetInterface *interface, uint16_t address)
1954 {
1955  uint16_t data;
1956  uint32_t command;
1957 
1958  //SPI slave mode?
1959  if(interface->spiDriver != NULL)
1960  {
1961  //Set up a read operation
1962  command = KSZ9897_SPI_CMD_READ;
1963  //Set register address
1964  command |= (address << 5) & KSZ9897_SPI_CMD_ADDR;
1965 
1966  //Pull the CS pin low
1967  interface->spiDriver->assertCs();
1968 
1969  //Write 32-bit command
1970  interface->spiDriver->transfer((command >> 24) & 0xFF);
1971  interface->spiDriver->transfer((command >> 16) & 0xFF);
1972  interface->spiDriver->transfer((command >> 8) & 0xFF);
1973  interface->spiDriver->transfer(command & 0xFF);
1974 
1975  //Read 16-bit data
1976  data = interface->spiDriver->transfer(0xFF) << 8;
1977  data |= interface->spiDriver->transfer(0xFF);
1978 
1979  //Terminate the operation by raising the CS pin
1980  interface->spiDriver->deassertCs();
1981  }
1982  else
1983  {
1984  //The MDC/MDIO interface does not have access to all the configuration
1985  //registers. It can only access the standard MIIM registers
1986  data = 0;
1987  }
1988 
1989  //Return register value
1990  return data;
1991 }
1992 
1993 
1994 /**
1995  * @brief Write switch register (32 bits)
1996  * @param[in] interface Underlying network interface
1997  * @param[in] address Switch register address
1998  * @param[in] data Register value
1999  **/
2000 
2002  uint32_t data)
2003 {
2004  uint32_t command;
2005 
2006  //SPI slave mode?
2007  if(interface->spiDriver != NULL)
2008  {
2009  //Set up a write operation
2010  command = KSZ9897_SPI_CMD_WRITE;
2011  //Set register address
2012  command |= (address << 5) & KSZ9897_SPI_CMD_ADDR;
2013 
2014  //Pull the CS pin low
2015  interface->spiDriver->assertCs();
2016 
2017  //Write 32-bit command
2018  interface->spiDriver->transfer((command >> 24) & 0xFF);
2019  interface->spiDriver->transfer((command >> 16) & 0xFF);
2020  interface->spiDriver->transfer((command >> 8) & 0xFF);
2021  interface->spiDriver->transfer(command & 0xFF);
2022 
2023  //Write 32-bit data
2024  interface->spiDriver->transfer((data >> 24) & 0xFF);
2025  interface->spiDriver->transfer((data >> 16) & 0xFF);
2026  interface->spiDriver->transfer((data >> 8) & 0xFF);
2027  interface->spiDriver->transfer(data & 0xFF);
2028 
2029  //Terminate the operation by raising the CS pin
2030  interface->spiDriver->deassertCs();
2031  }
2032  else
2033  {
2034  //The MDC/MDIO interface does not have access to all the configuration
2035  //registers. It can only access the standard MIIM registers
2036  }
2037 }
2038 
2039 
2040 /**
2041  * @brief Read switch register (32 bits)
2042  * @param[in] interface Underlying network interface
2043  * @param[in] address Switch register address
2044  * @return Register value
2045  **/
2046 
2047 uint32_t ksz9897ReadSwitchReg32(NetInterface *interface, uint16_t address)
2048 {
2049  uint32_t data;
2050  uint32_t command;
2051 
2052  //SPI slave mode?
2053  if(interface->spiDriver != NULL)
2054  {
2055  //Set up a read operation
2056  command = KSZ9897_SPI_CMD_READ;
2057  //Set register address
2058  command |= (address << 5) & KSZ9897_SPI_CMD_ADDR;
2059 
2060  //Pull the CS pin low
2061  interface->spiDriver->assertCs();
2062 
2063  //Write 32-bit command
2064  interface->spiDriver->transfer((command >> 24) & 0xFF);
2065  interface->spiDriver->transfer((command >> 16) & 0xFF);
2066  interface->spiDriver->transfer((command >> 8) & 0xFF);
2067  interface->spiDriver->transfer(command & 0xFF);
2068 
2069  //Read 32-bit data
2070  data = interface->spiDriver->transfer(0xFF) << 24;
2071  data |= interface->spiDriver->transfer(0xFF) << 16;
2072  data |= interface->spiDriver->transfer(0xFF) << 8;
2073  data |= interface->spiDriver->transfer(0xFF);
2074 
2075  //Terminate the operation by raising the CS pin
2076  interface->spiDriver->deassertCs();
2077  }
2078  else
2079  {
2080  //The MDC/MDIO interface does not have access to all the configuration
2081  //registers. It can only access the standard MIIM registers
2082  data = 0;
2083  }
2084 
2085  //Return register value
2086  return data;
2087 }
const uint16_t ksz9897IngressTailTag[8]
Tail tag rules (host to KSZ9897)
uint16_t ksz9897ReadMmdReg(NetInterface *interface, uint8_t port, uint8_t devAddr, uint16_t regAddr)
Read MMD register.
#define KSZ9897_STATIC_MCAST_TABLE_CTRL
void nicNotifyLinkChange(NetInterface *interface)
Process link state change notification.
Definition: nic.c:601
#define KSZ9897_SWITCH_LUE_CTRL1_FLUSH_MSTP_ENTRIES
#define KSZ9897_STATIC_TABLE_ENTRY4
@ NIC_LINK_SPEED_1GBPS
Definition: nic.h:113
void ksz9897WriteSwitchReg16(NetInterface *interface, uint16_t address, uint16_t data)
Write switch register (16 bits)
error_t ksz9897TagFrame(NetInterface *interface, NetBuffer *buffer, size_t *offset, NetTxAncillary *ancillary)
Add tail tag to Ethernet frame.
error_t ksz9897DeleteStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Remove an entry from the static MAC table.
#define KSZ9897_SWITCH_LUE_CTRL3_AGE_PERIOD_DEFAULT
#define KSZ9897_UNKONWN_MULTICAST_CTRL_FWD
#define NetContext
Definition: net.h:36
#define KSZ9897_ALU_TABLE_CTRL_VALID_ENTRY_OR_SEARCH_END
int bool_t
Definition: compiler_port.h:63
@ ERROR_NOT_FOUND
Definition: error.h:148
@ NIC_LINK_SPEED_UNKNOWN
Definition: nic.h:110
#define KSZ9897_SWITCH_LUE_CTRL0_AGE_COUNT_DEFAULT
uint32_t destPorts
Definition: nic.h:152
#define KSZ9897_ALU_TABLE_ENTRY2_PORT6_FORWARD
#define KSZ9897_BMSR
#define KSZ9897_PHYCON_DUPLEX_STATUS
#define KSZ9897_PORT6_XMII_CTRL1
#define KSZ9897_ALU_TABLE_ENTRY2_PORT7_FORWARD
#define KSZ9897_PORTn_XMII_CTRL1_IF_TYPE_RGMII
#define KSZ9897_MMDACR_DEVAD
void ksz9897EnableIgmpSnooping(NetInterface *interface, bool_t enable)
Enable IGMP snooping.
@ NIC_FULL_DUPLEX_MODE
Definition: nic.h:125
@ SWITCH_PORT_STATE_LISTENING
Definition: nic.h:138
#define KSZ9897_STATIC_MAC_TABLE_SIZE
error_t ksz9897Init(NetInterface *interface)
KSZ9897 Ethernet switch initialization.
void ksz9897EnableIrq(NetInterface *interface)
Enable interrupts.
#define KSZ9897_PORTn_MSTP_STATE_TRANSMIT_EN
#define KSZ9897_TAIL_TAG_DEST_PORT3
#define KSZ9897_PORT6_OP_CTRL0
void ksz9897WriteSwitchReg32(NetInterface *interface, uint16_t address, uint32_t data)
Write switch register (32 bits)
@ ERROR_END_OF_TABLE
Definition: error.h:292
@ SWITCH_PORT_STATE_DISABLED
Definition: nic.h:136
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
#define KSZ9897_MMDACR
#define TRUE
Definition: os_port.h:50
void ksz9897WriteSgmiiReg(NetInterface *interface, uint32_t address, uint16_t data)
Write SGMII register.
error_t ksz9897GetStaticFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the static MAC table.
uint8_t data[]
Definition: ethernet.h:224
#define KSZ9897_PHYCON_SPEED_100BTX
#define KSZ9897_TAIL_TAG_DEST_PORT7
@ SWITCH_PORT_STATE_LEARNING
Definition: nic.h:139
#define KSZ9897_PHYCON_SPEED_1000BT
#define KSZ9897_SWITCH_MAC_CTRL0
#define KSZ9897_ALU_TABLE_ENTRY2_PORT4_FORWARD
uint8_t type
Definition: coap_common.h:176
void ksz9897SetPortState(NetInterface *interface, uint8_t port, SwitchPortState state)
Set port state.
#define KSZ9897_ALU_TABLE_ENTRY1
void ksz9897WritePhyReg(NetInterface *interface, uint8_t port, uint8_t address, uint16_t data)
Write PHY register.
@ ERROR_INVALID_PORT
Definition: error.h:104
#define KSZ9897_PORTn_MSTP_STATE_LEARNING_DIS
#define KSZ9897_MMDAADR
#define KSZ9897_MMD_LED_MODE_RESERVED_DEFAULT
#define KSZ9897_SPI_CMD_READ
#define KSZ9897_PORTn_MSTP_STATE(port)
@ ERROR_TABLE_FULL
Definition: error.h:291
#define KSZ9897_PORT3
#define KSZ9897_STATIC_MCAST_TABLE_CTRL_TABLE_SELECT
#define KSZ9897_GLOBAL_PORT_MIRROR_SNOOP_CTRL_IGMP_SNOOP_EN
EthHeader
Definition: ethernet.h:225
#define KSZ9897_MMD_EEE_ADV
#define KSZ9897_SPI_CMD_ADDR
error_t ksz9897UntagFrame(NetInterface *interface, uint8_t **frame, size_t *length, NetRxAncillary *ancillary)
Decode tail tag from incoming Ethernet frame.
void ksz9897FlushStaticFdbTable(NetInterface *interface)
Flush static MAC table.
#define SMI_OPCODE_WRITE
Definition: nic.h:66
#define KSZ9897_MMDACR_FUNC_ADDR
#define KSZ9897_MMD_LED_MODE_LED_MODE_TRI_COLOR_DUAL
KSZ9897 7-port Gigabit Ethernet switch driver.
#define KSZ9897_PORTn_XMII_CTRL1_IF_TYPE
#define KSZ9897_STATIC_MCAST_TABLE_CTRL_ACTION
#define KSZ9897_PORT6_XMII_CTRL0
#define KSZ9897_PORT2
__weak_func uint32_t ksz9897GetPort7LinkSpeed(NetInterface *interface)
Get port 7 link speed.
void ksz9897DumpPhyReg(NetInterface *interface, uint8_t port)
Dump PHY registers for debugging purpose.
uint8_t ksz9897ReadSwitchReg8(NetInterface *interface, uint16_t address)
Read switch register (8 bits)
@ SWITCH_PORT_STATE_UNKNOWN
Definition: nic.h:135
#define KSZ9897_PORT_MASK
#define KSZ9897_PORTn_XMII_CTRL0_SPEED_10_100
#define FALSE
Definition: os_port.h:46
#define KSZ9897_PORTn_MSTP_STATE_RECEIVE_EN
__weak_func bool_t ksz9897GetPort7LinkState(NetInterface *interface)
Get port 7 link state.
#define KSZ9897_ALU_TABLE_ENTRY2_PORT2_FORWARD
uint16_t ksz9897ReadSwitchReg16(NetInterface *interface, uint16_t address)
Read switch register (16 bits)
error_t
Error codes.
Definition: error.h:43
void ksz9897SetUnknownMcastFwdPorts(NetInterface *interface, bool_t enable, uint32_t forwardPorts)
Set forward ports for unknown multicast packets.
#define KSZ9897_PORTn_XMII_CTRL0_DUPLEX
const SwitchDriver ksz9897SwitchDriver
KSZ9897 Ethernet switch driver.
#define KSZ9897_GLOBAL_PORT_MIRROR_SNOOP_CTRL
#define KSZ9897_PORTn_OP_CTRL0_TAIL_TAG_EN
#define KSZ9897_STATIC_TABLE_ENTRY2_PORT_FORWARD
#define KSZ9897_GLOBAL_PORT_MIRROR_SNOOP_CTRL_MLD_SNOOP_EN
#define KSZ9897_STATIC_TABLE_ENTRY1
#define NetRxAncillary
Definition: net_misc.h:40
#define KSZ9897_ALU_TABLE_CTRL_VALID
#define NetInterface
Definition: net.h:40
@ NIC_LINK_SPEED_10MBPS
Definition: nic.h:111
#define KSZ9897_ALU_TABLE_CTRL_START_FINISH
@ ERROR_INVALID_LENGTH
Definition: error.h:111
__weak_func void ksz9897EventHandler(NetInterface *interface)
KSZ9897 event handler.
#define KSZ9897_ALU_TABLE_ENTRY2_PORT1_FORWARD
error_t ethPadFrame(NetBuffer *buffer, size_t *length)
Ethernet frame padding.
uint32_t ksz9897GetLinkSpeed(NetInterface *interface, uint8_t port)
Get link speed.
#define KSZ9897_ALU_TABLE_CTRL_ACTION_SEARCH
#define KSZ9897_TAIL_TAG_DEST_PORT1
#define NetTxAncillary
Definition: net_misc.h:36
#define KSZ9897_STATIC_TABLE_ENTRY3
#define KSZ9897_UNKONWN_MULTICAST_CTRL_FWD_MAP
#define SMI_OPCODE_READ
Definition: nic.h:67
SwitchPortState
Switch port state.
Definition: nic.h:134
void ksz9897DisableIrq(NetInterface *interface)
Disable interrupts.
#define KSZ9897_SWITCH_LUE_CTRL2_FLUSH_OPTION_DYNAMIC
#define KSZ9897_TAIL_TAG_SRC_PORT
#define KSZ9897_ALU_TABLE_ENTRY2_PORT3_FORWARD
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
#define KSZ9897_PORT6
#define KSZ9897_STATIC_TABLE_ENTRY2_OVERRIDE
uint32_t ksz9897ReadSwitchReg32(NetInterface *interface, uint16_t address)
Read switch register (32 bits)
#define KSZ9897_ALU_TABLE_ENTRY2
#define MIN(a, b)
Definition: os_port.h:63
#define KSZ9897_PORT5
#define KSZ9897_BMSR_LINK_STATUS
__weak_func void ksz9897InitHook(NetInterface *interface)
KSZ9897 custom configuration.
#define KSZ9897_SWITCH_LUE_CTRL0
void ksz9897EnableRsvdMcastTable(NetInterface *interface, bool_t enable)
Enable reserved multicast table.
void ksz9897EnableMldSnooping(NetInterface *interface, bool_t enable)
Enable MLD snooping.
#define KSZ9897_PORT7
#define KSZ9897_PHYCON_SPEED_10BT
#define KSZ9897_SWITCH_LUE_CTRL2
#define KSZ9897_SWITCH_LUE_CTRL1_FLUSH_ALU_TABLE
@ SWITCH_PORT_STATE_FORWARDING
Definition: nic.h:140
#define KSZ9897_UNKONWN_MULTICAST_CTRL_FWD_MAP_ALL
#define KSZ9897_SWITCH_MAC_CTRL0_FRAME_LEN_CHECK_EN
#define KSZ9897_PORT7_SGMII_ADDR
uint16_t port
Definition: dns_common.h:270
#define KSZ9897_ALU_TABLE_CTRL
#define TRACE_DEBUG(...)
Definition: debug.h:119
#define KSZ9897_TAIL_TAG_DEST_PORT4
#define KSZ9897_MMDACR_FUNC_DATA_NO_POST_INC
#define KSZ9897_STATIC_TABLE_ENTRY1_VALID
#define KSZ9897_SWITCH_LUE_CTRL1
#define KSZ9897_SWITCH_OP_START_SWITCH
error_t ksz9897GetDynamicFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the dynamic MAC table.
uint16_t regAddr
Ethernet switch driver.
Definition: nic.h:325
#define KSZ9897_SWITCH_LUE_CTRL0_RESERVED_MCAST_LOOKUP_EN
#define KSZ9897_PORTn_XMII_CTRL1_RGMII_ID_EG
#define HTONS(value)
Definition: cpu_endian.h:410
uint8_t n
#define KSZ9897_UNKONWN_MULTICAST_CTRL
#define KSZ9897_TAIL_TAG_NORMAL_ADDR_LOOKUP
#define KSZ9897_PORTn_XMII_CTRL1_SPEED_1000
@ ERROR_INVALID_ENTRY
Definition: error.h:290
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:604
void ksz9897SetAgingTime(NetInterface *interface, uint32_t agingTime)
Set aging time for dynamic filtering entries.
Ipv6Addr address[]
Definition: ipv6.h:345
#define KSZ9897_PORT1
NicDuplexMode
Duplex mode.
Definition: nic.h:122
MacAddr macAddr
Definition: nic.h:150
#define KSZ9897_ALU_TABLE_ENTRY4
#define KSZ9897_MMD_LED_MODE
uint8_t srcPort
Definition: nic.h:151
@ NIC_HALF_DUPLEX_MODE
Definition: nic.h:124
#define KSZ9897_UNKONWN_MULTICAST_CTRL_FWD_MAP_PORT6
#define KSZ9897_TAIL_TAG_DEST_PORT2
uint8_t value[]
Definition: tcp.h:376
#define KSZ9897_ALU_TABLE_ENTRY3
#define KSZ9897_PORT4
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
uint16_t ksz9897ReadPhyReg(NetInterface *interface, uint8_t port, uint8_t address)
Read PHY register.
#define KSZ9897_PORT7_SGMII_DATA
@ NIC_UNKNOWN_DUPLEX_MODE
Definition: nic.h:123
#define KSZ9897_SWITCH_OP
#define KSZ9897_SWITCH_LUE_CTRL2_FLUSH_OPTION
#define KSZ9897_PORTn_XMII_CTRL1_RGMII_ID_IG
#define KSZ9897_TAIL_TAG_PORT_BLOCKING_OVERRIDE
bool_t ksz9897GetLinkState(NetInterface *interface, uint8_t port)
Get link state.
#define KSZ9897_SPI_CMD_WRITE
void ksz9897WriteSwitchReg8(NetInterface *interface, uint16_t address, uint8_t data)
Write switch register (8 bits)
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
#define KSZ9897_STATIC_MCAST_TABLE_CTRL_TABLE_INDEX
void ksz9897WriteMmdReg(NetInterface *interface, uint8_t port, uint8_t devAddr, uint16_t regAddr, uint16_t data)
Write MMD register.
#define KSZ9897_SWITCH_LUE_CTRL3
#define KSZ9897_STATIC_TABLE_ENTRY2
#define SWITCH_CPU_PORT_MASK
Definition: nic.h:60
@ NIC_LINK_SPEED_100MBPS
Definition: nic.h:112
SwitchPortState ksz9897GetPortState(NetInterface *interface, uint8_t port)
Get port state.
#define KSZ9897_ALU_TABLE_ENTRY2_PORT5_FORWARD
#define KSZ9897_SWITCH_LUE_CTRL0_HASH_OPTION_CRC
unsigned int uint_t
Definition: compiler_port.h:57
TCP/IP stack core.
__weak_func NicDuplexMode ksz9897GetPort7DuplexMode(NetInterface *interface)
Get port 7 duplex mode.
#define KSZ9897_CHIP_ID1_DEFAULT
#define KSZ9897_PHYCON
__weak_func void ksz9897Tick(NetInterface *interface)
KSZ9897 timer handler.
#define KSZ9897_TAIL_TAG_DEST_PORT5
Helper functions for Ethernet.
uint16_t ksz9897ReadSgmiiReg(NetInterface *interface, uint32_t address)
Read SGMII register.
NicDuplexMode ksz9897GetDuplexMode(NetInterface *interface, uint8_t port)
Get duplex mode.
void ksz9897FlushDynamicFdbTable(NetInterface *interface, uint8_t port)
Flush dynamic MAC table.
#define KSZ9897_ALU_TABLE_ENTRY2_PORT_FORWARD
@ NO_ERROR
Success.
Definition: error.h:44
bool_t override
Definition: nic.h:153
#define KSZ9897_PORT6_MASK
Debugging facilities.
Forwarding database entry.
Definition: nic.h:149
#define KSZ9897_PORTn_ETH_PHY_REG(port, addr)
#define KSZ9897_STATIC_MCAST_TABLE_CTRL_START_FINISH
error_t ksz9897AddStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Add a new entry to the static MAC table.
#define KSZ9897_CHIP_ID1