ksz9896_driver.c
Go to the documentation of this file.
1 /**
2  * @file ksz9896_driver.c
3  * @brief KSZ9896 6-port Gigabit Ethernet switch driver
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 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.4.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 KSZ9896 Ethernet switch driver
43  **/
44 
46 {
70 };
71 
72 
73 /**
74  * @brief Tail tag rules (host to KSZ9896)
75  **/
76 
77 const uint16_t ksz9896IngressTailTag[6] =
78 {
85 };
86 
87 
88 /**
89  * @brief KSZ9896 Ethernet switch initialization
90  * @param[in] interface Underlying network interface
91  * @return Error code
92  **/
93 
95 {
96  uint_t port;
97  uint8_t temp;
98 
99  //Debug message
100  TRACE_INFO("Initializing KSZ9896...\r\n");
101 
102  //SPI slave mode?
103  if(interface->spiDriver != NULL)
104  {
105  //Initialize SPI interface
106  interface->spiDriver->init();
107 
108  //Wait for the serial interface to be ready
109  do
110  {
111  //Read CHIP_ID1 register
112  temp = ksz9896ReadSwitchReg8(interface, KSZ9896_CHIP_ID1);
113 
114  //The returned data is invalid until the serial interface is ready
115  } while(temp != KSZ9896_CHIP_ID1_DEFAULT);
116 
117 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
118  //Enable tail tag feature
122 
123  //Disable frame length check (silicon errata workaround 12)
127 #else
128  //Disable tail tag feature
132 
133  //Enable frame length check
137 #endif
138 
139  //Loop through the ports
141  {
142 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
143  //Port separation mode?
144  if(interface->port != 0)
145  {
146  //Disable packet transmission and address learning
148  }
149  else
150 #endif
151  {
152  //Enable transmission, reception and address learning
154  }
155  }
156 
157  //Restore default age count
161 
162  //Restore default age period
165 
166  //Add internal delay to ingress and egress RGMII clocks
171 
172  //Start switch operation
175  }
176  else if(interface->smiDriver != NULL)
177  {
178  //Initialize serial management interface
179  interface->smiDriver->init();
180  }
181  else
182  {
183  //Just for sanity
184  }
185 
186  //Loop through the ports
188  {
189  //Improve PHY receive performance (silicon errata workaround 1)
190  ksz9896WriteMmdReg(interface, port, 0x01, 0x6F, 0xDD0B);
191  ksz9896WriteMmdReg(interface, port, 0x01, 0x8F, 0x6032);
192  ksz9896WriteMmdReg(interface, port, 0x01, 0x9D, 0x248C);
193  ksz9896WriteMmdReg(interface, port, 0x01, 0x75, 0x0060);
194  ksz9896WriteMmdReg(interface, port, 0x01, 0xD3, 0x7777);
195  ksz9896WriteMmdReg(interface, port, 0x1C, 0x06, 0x3008);
196  ksz9896WriteMmdReg(interface, port, 0x1C, 0x08, 0x2001);
197 
198  //Improve transmit waveform amplitude (silicon errata workaround 2)
199  ksz9896WriteMmdReg(interface, port, 0x1C, 0x04, 0x00D0);
200 
201  //EEE must be manually disabled (silicon errata workaround 3)
203 
204  //Adjust power supply settings (silicon errata workaround 6)
205  ksz9896WriteMmdReg(interface, port, 0x1C, 0x13, 0x6EFF);
206  ksz9896WriteMmdReg(interface, port, 0x1C, 0x14, 0xE6FF);
207  ksz9896WriteMmdReg(interface, port, 0x1C, 0x15, 0x6EFF);
208  ksz9896WriteMmdReg(interface, port, 0x1C, 0x16, 0xE6FF);
209  ksz9896WriteMmdReg(interface, port, 0x1C, 0x17, 0x00FF);
210  ksz9896WriteMmdReg(interface, port, 0x1C, 0x18, 0x43FF);
211  ksz9896WriteMmdReg(interface, port, 0x1C, 0x19, 0xC3FF);
212  ksz9896WriteMmdReg(interface, port, 0x1C, 0x1A, 0x6FFF);
213  ksz9896WriteMmdReg(interface, port, 0x1C, 0x1B, 0x07FF);
214  ksz9896WriteMmdReg(interface, port, 0x1C, 0x1C, 0x0FFF);
215  ksz9896WriteMmdReg(interface, port, 0x1C, 0x1D, 0xE7FF);
216  ksz9896WriteMmdReg(interface, port, 0x1C, 0x1E, 0xEFFF);
217  ksz9896WriteMmdReg(interface, port, 0x1C, 0x20, 0xEEEE);
218 
219  //Select tri-color dual-LED mode (silicon errata workaround 14)
223 
224  //Debug message
225  TRACE_DEBUG("Port %u:\r\n", port);
226  //Dump PHY registers for debugging purpose
227  ksz9896DumpPhyReg(interface, port);
228  }
229 
230  //Perform custom configuration
231  ksz9896InitHook(interface);
232 
233  //Force the TCP/IP stack to poll the link state at startup
234  interface->phyEvent = TRUE;
235  //Notify the TCP/IP stack of the event
237 
238  //Successful initialization
239  return NO_ERROR;
240 }
241 
242 
243 /**
244  * @brief KSZ9896 custom configuration
245  * @param[in] interface Underlying network interface
246  **/
247 
248 __weak_func void ksz9896InitHook(NetInterface *interface)
249 {
250 }
251 
252 
253 /**
254  * @brief KSZ9896 timer handler
255  * @param[in] interface Underlying network interface
256  **/
257 
258 void ksz9896Tick(NetInterface *interface)
259 {
260  uint_t port;
261  bool_t linkState;
262 
263 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
264  //Port separation mode?
265  if(interface->port != 0)
266  {
267  uint_t i;
268  NetInterface *virtualInterface;
269 
270  //Loop through network interfaces
271  for(i = 0; i < NET_INTERFACE_COUNT; i++)
272  {
273  //Point to the current interface
274  virtualInterface = &netInterface[i];
275 
276  //Check whether the current virtual interface is attached to the
277  //physical interface
278  if(virtualInterface == interface ||
279  virtualInterface->parent == interface)
280  {
281  //Retrieve current link state
282  linkState = ksz9896GetLinkState(interface, virtualInterface->port);
283 
284  //Link up or link down event?
285  if(linkState != virtualInterface->linkState)
286  {
287  //Set event flag
288  interface->phyEvent = TRUE;
289  //Notify the TCP/IP stack of the event
291  }
292  }
293  }
294  }
295  else
296 #endif
297  {
298  //Initialize link state
299  linkState = FALSE;
300 
301  //Loop through the ports
303  {
304  //Retrieve current link state
305  if(ksz9896GetLinkState(interface, port))
306  {
307  linkState = TRUE;
308  }
309  }
310 
311  //Link up or link down event?
312  if(linkState != interface->linkState)
313  {
314  //Set event flag
315  interface->phyEvent = TRUE;
316  //Notify the TCP/IP stack of the event
318  }
319  }
320 }
321 
322 
323 /**
324  * @brief Enable interrupts
325  * @param[in] interface Underlying network interface
326  **/
327 
329 {
330 }
331 
332 
333 /**
334  * @brief Disable interrupts
335  * @param[in] interface Underlying network interface
336  **/
337 
339 {
340 }
341 
342 
343 /**
344  * @brief KSZ9896 event handler
345  * @param[in] interface Underlying network interface
346  **/
347 
349 {
350  uint_t port;
351  bool_t linkState;
352 
353 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
354  //Port separation mode?
355  if(interface->port != 0)
356  {
357  uint_t i;
358  NetInterface *virtualInterface;
359 
360  //Loop through network interfaces
361  for(i = 0; i < NET_INTERFACE_COUNT; i++)
362  {
363  //Point to the current interface
364  virtualInterface = &netInterface[i];
365 
366  //Check whether the current virtual interface is attached to the
367  //physical interface
368  if(virtualInterface == interface ||
369  virtualInterface->parent == interface)
370  {
371  //Get the port number associated with the current interface
372  port = virtualInterface->port;
373 
374  //Valid port?
375  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
376  {
377  //Retrieve current link state
378  linkState = ksz9896GetLinkState(interface, port);
379 
380  //Link up event?
381  if(linkState && !virtualInterface->linkState)
382  {
383  //Retrieve host interface speed
384  interface->linkSpeed = ksz9896GetLinkSpeed(interface,
385  KSZ9896_PORT6);
386 
387  //Retrieve host interface duplex mode
388  interface->duplexMode = ksz9896GetDuplexMode(interface,
389  KSZ9896_PORT6);
390 
391  //Adjust MAC configuration parameters for proper operation
392  interface->nicDriver->updateMacConfig(interface);
393 
394  //Check current speed
395  virtualInterface->linkSpeed = ksz9896GetLinkSpeed(interface,
396  port);
397 
398  //Check current duplex mode
399  virtualInterface->duplexMode = ksz9896GetDuplexMode(interface,
400  port);
401 
402  //Update link state
403  virtualInterface->linkState = TRUE;
404 
405  //Process link state change event
406  nicNotifyLinkChange(virtualInterface);
407  }
408  //Link down event
409  else if(!linkState && virtualInterface->linkState)
410  {
411  //Update link state
412  virtualInterface->linkState = FALSE;
413 
414  //Process link state change event
415  nicNotifyLinkChange(virtualInterface);
416  }
417  }
418  }
419  }
420  }
421  else
422 #endif
423  {
424  //Initialize link state
425  linkState = FALSE;
426 
427  //Loop through the ports
429  {
430  //Retrieve current link state
431  if(ksz9896GetLinkState(interface, port))
432  {
433  linkState = TRUE;
434  }
435  }
436 
437  //Link up event?
438  if(linkState)
439  {
440  //Retrieve host interface speed
441  interface->linkSpeed = ksz9896GetLinkSpeed(interface, KSZ9896_PORT6);
442  //Retrieve host interface duplex mode
443  interface->duplexMode = ksz9896GetDuplexMode(interface, KSZ9896_PORT6);
444 
445  //Adjust MAC configuration parameters for proper operation
446  interface->nicDriver->updateMacConfig(interface);
447 
448  //Update link state
449  interface->linkState = TRUE;
450  }
451  else
452  {
453  //Update link state
454  interface->linkState = FALSE;
455  }
456 
457  //Process link state change event
458  nicNotifyLinkChange(interface);
459  }
460 }
461 
462 
463 /**
464  * @brief Add tail tag to Ethernet frame
465  * @param[in] interface Underlying network interface
466  * @param[in] buffer Multi-part buffer containing the payload
467  * @param[in,out] offset Offset to the first payload byte
468  * @param[in] ancillary Additional options passed to the stack along with
469  * the packet
470  * @return Error code
471  **/
472 
474  size_t *offset, NetTxAncillary *ancillary)
475 {
476  error_t error;
477 
478  //Initialize status code
479  error = NO_ERROR;
480 
481 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
482  //SPI slave mode?
483  if(interface->spiDriver != NULL)
484  {
485  //Valid port?
486  if(ancillary->port <= KSZ9896_PORT5)
487  {
488  size_t length;
489  const uint16_t *tailTag;
490 
491  //The two-byte tail tagging is used to indicate the destination port
492  tailTag = &ksz9896IngressTailTag[ancillary->port];
493 
494  //Retrieve the length of the Ethernet frame
495  length = netBufferGetLength(buffer) - *offset;
496 
497  //The host controller should manually add padding to the packet before
498  //inserting the tail tag
499  error = ethPadFrame(buffer, &length);
500 
501  //Check status code
502  if(!error)
503  {
504  //The tail tag is inserted at the end of the packet, just before
505  //the CRC
506  error = netBufferAppend(buffer, tailTag, sizeof(uint16_t));
507  }
508  }
509  else
510  {
511  //The port number is not valid
512  error = ERROR_INVALID_PORT;
513  }
514  }
515 #endif
516 
517  //Return status code
518  return error;
519 }
520 
521 
522 /**
523  * @brief Decode tail tag from incoming Ethernet frame
524  * @param[in] interface Underlying network interface
525  * @param[in,out] frame Pointer to the received Ethernet frame
526  * @param[in,out] length Length of the frame, in bytes
527  * @param[in,out] ancillary Additional options passed to the stack along with
528  * the packet
529  * @return Error code
530  **/
531 
532 error_t ksz9896UntagFrame(NetInterface *interface, uint8_t **frame,
533  size_t *length, NetRxAncillary *ancillary)
534 {
535  error_t error;
536 
537  //Initialize status code
538  error = NO_ERROR;
539 
540 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
541  //SPI slave mode?
542  if(interface->spiDriver != NULL)
543  {
544  //Valid Ethernet frame received?
545  if(*length >= (sizeof(EthHeader) + sizeof(uint8_t)))
546  {
547  uint8_t *tailTag;
548 
549  //The tail tag is inserted at the end of the packet, just before
550  //the CRC
551  tailTag = *frame + *length - sizeof(uint8_t);
552 
553  //The one byte tail tagging is used to indicate the source port
554  ancillary->port = (*tailTag & KSZ9896_TAIL_TAG_SRC_PORT) + 1;
555 
556  //Strip tail tag from Ethernet frame
557  *length -= sizeof(uint8_t);
558  }
559  else
560  {
561  //Drop the received frame
562  error = ERROR_INVALID_LENGTH;
563  }
564  }
565  else
566  {
567  //Tail tagging mode cannot be enabled through MDC/MDIO interface
568  ancillary->port = 0;
569  }
570 #endif
571 
572  //Return status code
573  return error;
574 }
575 
576 
577 /**
578  * @brief Get link state
579  * @param[in] interface Underlying network interface
580  * @param[in] port Port number
581  * @return Link state
582  **/
583 
585 {
586  uint16_t value;
587  bool_t linkState;
588 
589  //Check port number
590  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
591  {
592  //Any link failure condition is latched in the BMSR register. Reading
593  //the register twice will always return the actual link status
594  value = ksz9896ReadPhyReg(interface, port, KSZ9896_BMSR);
595  value = ksz9896ReadPhyReg(interface, port, KSZ9896_BMSR);
596 
597  //Retrieve current link state
598  linkState = (value & KSZ9896_BMSR_LINK_STATUS) ? TRUE : FALSE;
599  }
600  else
601  {
602  //The specified port number is not valid
603  linkState = FALSE;
604  }
605 
606  //Return link status
607  return linkState;
608 }
609 
610 
611 /**
612  * @brief Get link speed
613  * @param[in] interface Underlying network interface
614  * @param[in] port Port number
615  * @return Link speed
616  **/
617 
618 uint32_t ksz9896GetLinkSpeed(NetInterface *interface, uint8_t port)
619 {
620  uint8_t type;
621  uint16_t value;
622  uint32_t linkSpeed;
623 
624  //Check port number
625  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
626  {
627  //Read PHY control register
629 
630  //Retrieve current link speed
632  {
633  //1000BASE-T
634  linkSpeed = NIC_LINK_SPEED_1GBPS;
635  }
636  else if((value & KSZ9896_PHYCON_SPEED_100BTX) != 0)
637  {
638  //100BASE-TX
639  linkSpeed = NIC_LINK_SPEED_100MBPS;
640  }
641  else if((value & KSZ9896_PHYCON_SPEED_10BT) != 0)
642  {
643  //10BASE-T
644  linkSpeed = NIC_LINK_SPEED_10MBPS;
645  }
646  else
647  {
648  //The link speed is not valid
649  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
650  }
651  }
652  else if(port == KSZ9896_PORT6)
653  {
654  //SPI slave mode?
655  if(interface->spiDriver != NULL)
656  {
657  //Read port 6 XMII control 1 register
659 
660  //Retrieve host interface type
662 
663  //Gigabit interface?
666  {
667  //1000 Mb/s mode
668  linkSpeed = NIC_LINK_SPEED_1GBPS;
669  }
670  else
671  {
672  //Read port 6 XMII control 0 register
674 
675  //Retrieve host interface speed
677  {
678  //100 Mb/s mode
679  linkSpeed = NIC_LINK_SPEED_100MBPS;
680  }
681  else
682  {
683  //10 Mb/s mode
684  linkSpeed = NIC_LINK_SPEED_10MBPS;
685  }
686  }
687  }
688  else
689  {
690  //The MDC/MDIO interface does not have access to all the configuration
691  //registers. It can only access the standard MIIM registers
692  linkSpeed = NIC_LINK_SPEED_100MBPS;
693  }
694  }
695  else
696  {
697  //The specified port number is not valid
698  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
699  }
700 
701  //Return link status
702  return linkSpeed;
703 }
704 
705 
706 /**
707  * @brief Get duplex mode
708  * @param[in] interface Underlying network interface
709  * @param[in] port Port number
710  * @return Duplex mode
711  **/
712 
714 {
715  uint16_t value;
716  NicDuplexMode duplexMode;
717 
718  //Check port number
719  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
720  {
721  //Read PHY control register
723 
724  //Retrieve current duplex mode
726  {
727  duplexMode = NIC_FULL_DUPLEX_MODE;
728  }
729  else
730  {
731  duplexMode = NIC_HALF_DUPLEX_MODE;
732  }
733  }
734  else if(port == KSZ9896_PORT6)
735  {
736  //SPI slave mode?
737  if(interface->spiDriver != NULL)
738  {
739  //Read port 6 XMII control 0 register
741 
742  //Retrieve host interface duplex mode
744  {
745  duplexMode = NIC_FULL_DUPLEX_MODE;
746  }
747  else
748  {
749  duplexMode = NIC_HALF_DUPLEX_MODE;
750  }
751  }
752  else
753  {
754  //The MDC/MDIO interface does not have access to all the configuration
755  //registers. It can only access the standard MIIM registers
756  duplexMode = NIC_FULL_DUPLEX_MODE;
757  }
758  }
759  else
760  {
761  //The specified port number is not valid
762  duplexMode = NIC_UNKNOWN_DUPLEX_MODE;
763  }
764 
765  //Return duplex mode
766  return duplexMode;
767 }
768 
769 
770 /**
771  * @brief Set port state
772  * @param[in] interface Underlying network interface
773  * @param[in] port Port number
774  * @param[in] state Port state
775  **/
776 
777 void ksz9896SetPortState(NetInterface *interface, uint8_t port,
778  SwitchPortState state)
779 {
780  uint8_t temp;
781 
782  //Check port number
783  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
784  {
785  //Read MSTP state register
787 
788  //Update port state
789  switch(state)
790  {
791  //Listening state
796  break;
797 
798  //Learning state
803  break;
804 
805  //Forwarding state
810  break;
811 
812  //Disabled state
813  default:
817  break;
818  }
819 
820  //Write the value back to MSTP state register
822  }
823 }
824 
825 
826 /**
827  * @brief Get port state
828  * @param[in] interface Underlying network interface
829  * @param[in] port Port number
830  * @return Port state
831  **/
832 
834 {
835  uint8_t temp;
836  SwitchPortState state;
837 
838  //Check port number
839  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
840  {
841  //Read MSTP state register
843 
844  //Check port state
845  if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
846  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) == 0 &&
848  {
849  //Disabled state
851  }
852  else if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
853  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) != 0 &&
855  {
856  //Listening state
858  }
859  else if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
860  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) == 0 &&
862  {
863  //Learning state
865  }
866  else if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) != 0 &&
867  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) != 0 &&
869  {
870  //Forwarding state
872  }
873  else
874  {
875  //Unknown state
877  }
878  }
879  else
880  {
881  //The specified port number is not valid
883  }
884 
885  //Return port state
886  return state;
887 }
888 
889 
890 /**
891  * @brief Set aging time for dynamic filtering entries
892  * @param[in] interface Underlying network interface
893  * @param[in] agingTime Aging time, in seconds
894  **/
895 
896 void ksz9896SetAgingTime(NetInterface *interface, uint32_t agingTime)
897 {
898  //The Age Period in combination with the Age Count field determines the
899  //aging time of dynamic entries in the address lookup table
900  agingTime = (agingTime + 3) / 4;
901 
902  //Limit the range of the parameter
903  agingTime = MIN(agingTime, 255);
904 
905  //Write the value to Switch Lookup Engine Control 3 register
907  (uint8_t) agingTime);
908 }
909 
910 
911 /**
912  * @brief Enable IGMP snooping
913  * @param[in] interface Underlying network interface
914  * @param[in] enable Enable or disable IGMP snooping
915  **/
916 
918 {
919  uint8_t temp;
920 
921  //Read the Global Port Mirroring and Snooping Control register
922  temp = ksz9896ReadSwitchReg8(interface,
924 
925  //Enable or disable IGMP snooping
926  if(enable)
927  {
929  }
930  else
931  {
933  }
934 
935  //Write the value back to Global Port Mirroring and Snooping Control register
937  temp);
938 }
939 
940 
941 /**
942  * @brief Enable MLD snooping
943  * @param[in] interface Underlying network interface
944  * @param[in] enable Enable or disable MLD snooping
945  **/
946 
948 {
949  uint8_t temp;
950 
951  //Read the Global Port Mirroring and Snooping Control register
952  temp = ksz9896ReadSwitchReg8(interface,
954 
955  //Enable or disable MLD snooping
956  if(enable)
957  {
959  }
960  else
961  {
963  }
964 
965  //Write the value back to Global Port Mirroring and Snooping Control register
967  temp);
968 }
969 
970 
971 /**
972  * @brief Enable reserved multicast table
973  * @param[in] interface Underlying network interface
974  * @param[in] enable Enable or disable reserved group addresses
975  **/
976 
978 {
979  uint8_t temp;
980 
981  //Read the Switch Lookup Engine Control 0 register
983 
984  //Enable or disable the reserved multicast table
985  if(enable)
986  {
988  }
989  else
990  {
992  }
993 
994  //Write the value back to Switch Lookup Engine Control 0 register
996 }
997 
998 
999 /**
1000  * @brief Add a new entry to the static MAC table
1001  * @param[in] interface Underlying network interface
1002  * @param[in] entry Pointer to the forwarding database entry
1003  * @return Error code
1004  **/
1005 
1007  const SwitchFdbEntry *entry)
1008 {
1009  error_t error;
1010  uint_t i;
1011  uint_t j;
1012  uint32_t value;
1013  SwitchFdbEntry currentEntry;
1014 
1015  //Keep track of the first free entry
1017 
1018  //Loop through the static MAC table
1019  for(i = 0; i < KSZ9896_STATIC_MAC_TABLE_SIZE; i++)
1020  {
1021  //Read current entry
1022  error = ksz9896GetStaticFdbEntry(interface, i, &currentEntry);
1023 
1024  //Valid entry?
1025  if(!error)
1026  {
1027  //Check whether the table already contains the specified MAC address
1028  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
1029  {
1030  j = i;
1031  break;
1032  }
1033  }
1034  else
1035  {
1036  //Keep track of the first free entry
1038  {
1039  j = i;
1040  }
1041  }
1042  }
1043 
1044  //Any entry available?
1046  {
1047  //Write the Static Address Table Entry 1 register
1050 
1051  //Set the relevant forward ports
1052  if(entry->destPorts == SWITCH_CPU_PORT_MASK)
1053  {
1055  }
1056  else
1057  {
1058  value = entry->destPorts & KSZ9896_PORT_MASK;
1059  }
1060 
1061  //Enable overriding of port state
1062  if(entry->override)
1063  {
1065  }
1066 
1067  //Write the Static Address Table Entry 2 register
1069 
1070  //Copy MAC address (first 16 bits)
1071  value = (entry->macAddr.b[0] << 8) | entry->macAddr.b[1];
1072 
1073  //Write the Static Address Table Entry 3 register
1075 
1076  //Copy MAC address (last 32 bits)
1077  value = (entry->macAddr.b[2] << 24) | (entry->macAddr.b[3] << 16) |
1078  (entry->macAddr.b[4] << 8) | entry->macAddr.b[5];
1079 
1080  //Write the Static Address Table Entry 4 register
1082 
1083  //Write the TABLE_INDEX field with the 4-bit index value
1085  //Set the TABLE_SELECT bit to 0 to select the static address table
1087  //Set the ACTION bit to 0 to indicate a write operation
1089  //Set the START_FINISH bit to 1 to initiate the operation
1091 
1092  //Start the write operation
1094  value);
1095 
1096  //When the operation is complete, the START_FINISH bit will be cleared
1097  //automatically
1098  do
1099  {
1100  //Read the Static Address and Reserved Multicast Table Control register
1101  value = ksz9896ReadSwitchReg32(interface,
1103 
1104  //Poll the START_FINISH bit
1106 
1107  //Successful processing
1108  error = NO_ERROR;
1109  }
1110  else
1111  {
1112  //The static MAC table is full
1113  error = ERROR_TABLE_FULL;
1114  }
1115 
1116  //Return status code
1117  return error;
1118 }
1119 
1120 
1121 /**
1122  * @brief Remove an entry from the static MAC table
1123  * @param[in] interface Underlying network interface
1124  * @param[in] entry Forwarding database entry to remove from the table
1125  * @return Error code
1126  **/
1127 
1129  const SwitchFdbEntry *entry)
1130 {
1131  error_t error;
1132  uint_t j;
1133  uint32_t value;
1134  SwitchFdbEntry currentEntry;
1135 
1136  //Loop through the static MAC table
1137  for(j = 0; j < KSZ9896_STATIC_MAC_TABLE_SIZE; j++)
1138  {
1139  //Read current entry
1140  error = ksz9896GetStaticFdbEntry(interface, j, &currentEntry);
1141 
1142  //Valid entry?
1143  if(!error)
1144  {
1145  //Check whether the table contains the specified MAC address
1146  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
1147  {
1148  break;
1149  }
1150  }
1151  }
1152 
1153  //Any matching entry?
1155  {
1156  //Clear Static Address Table Entry registers
1161 
1162  //Write the TABLE_INDEX field with the 4-bit index value
1164  //Set the TABLE_SELECT bit to 0 to select the static address table
1166  //Set the ACTION bit to 0 to indicate a write operation
1168  //Set the START_FINISH bit to 1 to initiate the operation
1170 
1171  //Start the write operation
1173  value);
1174 
1175  //When the operation is complete, the START_FINISH bit will be cleared
1176  //automatically
1177  do
1178  {
1179  //Read the Static Address and Reserved Multicast Table Control register
1180  value = ksz9896ReadSwitchReg32(interface,
1182 
1183  //Poll the START_FINISH bit
1185 
1186  //Successful processing
1187  error = NO_ERROR;
1188  }
1189  else
1190  {
1191  //The static MAC table does not contain the specified address
1192  error = ERROR_NOT_FOUND;
1193  }
1194 
1195  //Return status code
1196  return error;
1197 }
1198 
1199 
1200 /**
1201  * @brief Read an entry from the static MAC table
1202  * @param[in] interface Underlying network interface
1203  * @param[in] index Zero-based index of the entry to read
1204  * @param[out] entry Pointer to the forwarding database entry
1205  * @return Error code
1206  **/
1207 
1209  SwitchFdbEntry *entry)
1210 {
1211  error_t error;
1212  uint32_t value;
1213 
1214  //Check index parameter
1215  if(index < KSZ9896_STATIC_MAC_TABLE_SIZE)
1216  {
1217  //Write the TABLE_INDEX field with the 4-bit index value
1219  //Set the TABLE_SELECT bit to 0 to select the static address table
1221  //Set the ACTION bit to 1 to indicate a read operation
1223  //Set the START_FINISH bit to 1 to initiate the operation
1225 
1226  //Start the read operation
1228  value);
1229 
1230  //When the operation is complete, the START_FINISH bit will be cleared
1231  //automatically
1232  do
1233  {
1234  //Read the Static Address and Reserved Multicast Table Control register
1235  value = ksz9896ReadSwitchReg32(interface,
1237 
1238  //Poll the START_FINISH bit
1240 
1241  //Read the Static Address Table Entry 1 register
1243 
1244  //Valid entry?
1246  {
1247  //Read the Static Address Table Entry 2 register
1249 
1250  //Retrieve the ports associated with this MAC address
1251  entry->srcPort = 0;
1253 
1254  //Check the value of the OVERRIDE bit
1256  {
1257  entry->override = TRUE;
1258  }
1259  else
1260  {
1261  entry->override = FALSE;
1262  }
1263 
1264  //Read the Static Address Table Entry 3 register
1266 
1267  //Copy MAC address (first 16 bits)
1268  entry->macAddr.b[0] = (value >> 8) & 0xFF;
1269  entry->macAddr.b[1] = value & 0xFF;
1270 
1271  //Read the Static Address Table Entry 4 register
1273 
1274  //Copy MAC address (last 32 bits)
1275  entry->macAddr.b[2] = (value >> 24) & 0xFF;
1276  entry->macAddr.b[3] = (value >> 16) & 0xFF;
1277  entry->macAddr.b[4] = (value >> 8) & 0xFF;
1278  entry->macAddr.b[5] = value & 0xFF;
1279 
1280  //Successful processing
1281  error = NO_ERROR;
1282  }
1283  else
1284  {
1285  //The entry is not valid
1286  error = ERROR_INVALID_ENTRY;
1287  }
1288  }
1289  else
1290  {
1291  //The end of the table has been reached
1292  error = ERROR_END_OF_TABLE;
1293  }
1294 
1295  //Return status code
1296  return error;
1297 }
1298 
1299 
1300 /**
1301  * @brief Flush static MAC table
1302  * @param[in] interface Underlying network interface
1303  **/
1304 
1306 {
1307  uint_t i;
1308  uint32_t value;
1309 
1310  //Loop through the static MAC table
1311  for(i = 0; i < KSZ9896_STATIC_MAC_TABLE_SIZE; i++)
1312  {
1313  //Clear Static Address Table Entry registers
1318 
1319  //Write the TABLE_INDEX field with the 4-bit index value
1321  //Set the TABLE_SELECT bit to 0 to select the static address table
1323  //Set the ACTION bit to 0 to indicate a write operation
1325  //Set the START_FINISH bit to 1 to initiate the operation
1327 
1328  //Start the write operation
1330  value);
1331 
1332  //When the operation is complete, the START_FINISH bit will be cleared
1333  //automatically
1334  do
1335  {
1336  //Read the Static Address and Reserved Multicast Table Control register
1337  value = ksz9896ReadSwitchReg32(interface,
1339 
1340  //Poll the START_FINISH bit
1342  }
1343 }
1344 
1345 
1346 /**
1347  * @brief Read an entry from the dynamic MAC table
1348  * @param[in] interface Underlying network interface
1349  * @param[in] index Zero-based index of the entry to read
1350  * @param[out] entry Pointer to the forwarding database entry
1351  * @return Error code
1352  **/
1353 
1355  SwitchFdbEntry *entry)
1356 {
1357  error_t error;
1358  uint32_t value;
1359 
1360  //First entry?
1361  if(index == 0)
1362  {
1363  //Clear the ALU Table Access Control register to stop any operation
1365 
1366  //Start the search operation
1370  }
1371 
1372  //Poll the VALID_ENTRY_OR_SEARCH_END bit until it is set
1373  do
1374  {
1375  //Read the ALU Table Access Control register
1377 
1378  //This bit goes high to indicate either a new valid entry is returned or
1379  //the search is complete
1381 
1382  //Check whether the next valid entry is ready
1383  if((value & KSZ9896_ALU_TABLE_CTRL_VALID) != 0)
1384  {
1385  //Store the data from the ALU table entry
1386  entry->destPorts = 0;
1387  entry->override = FALSE;
1388 
1389  //Read the ALU Table Entry 1 and 2 registers
1392 
1393  //Retrieve the port associated with this MAC address
1395  {
1397  entry->srcPort = KSZ9896_PORT1;
1398  break;
1400  entry->srcPort = KSZ9896_PORT2;
1401  break;
1403  entry->srcPort = KSZ9896_PORT3;
1404  break;
1406  entry->srcPort = KSZ9896_PORT4;
1407  break;
1409  entry->srcPort = KSZ9896_PORT5;
1410  break;
1412  entry->srcPort = KSZ9896_PORT6;
1413  break;
1414  default:
1415  entry->srcPort = 0;
1416  break;
1417  }
1418 
1419  //Read the ALU Table Entry 3 register
1421 
1422  //Copy MAC address (first 16 bits)
1423  entry->macAddr.b[0] = (value >> 8) & 0xFF;
1424  entry->macAddr.b[1] = value & 0xFF;
1425 
1426  //Read the ALU Table Entry 4 register
1428 
1429  //Copy MAC address (last 32 bits)
1430  entry->macAddr.b[2] = (value >> 24) & 0xFF;
1431  entry->macAddr.b[3] = (value >> 16) & 0xFF;
1432  entry->macAddr.b[4] = (value >> 8) & 0xFF;
1433  entry->macAddr.b[5] = value & 0xFF;
1434 
1435  //Successful processing
1436  error = NO_ERROR;
1437  }
1438  else
1439  {
1440  //The search can be stopped any time by setting the START_FINISH bit to 0
1442 
1443  //The end of the table has been reached
1444  error = ERROR_END_OF_TABLE;
1445  }
1446 
1447  //Return status code
1448  return error;
1449 }
1450 
1451 
1452 /**
1453  * @brief Flush dynamic MAC table
1454  * @param[in] interface Underlying network interface
1455  * @param[in] port Port number
1456  **/
1457 
1459 {
1460  uint_t temp;
1461  uint8_t state;
1462 
1463  //Flush only dynamic table entries
1468 
1469  //Valid port number?
1470  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT6)
1471  {
1472  //Save the current state of the port
1474 
1475  //Turn off learning capability
1478 
1479  //All the entries associated with a port that has its learning capability
1480  //being turned off will be flushed
1484 
1485  //Restore the original state of the port
1487  }
1488  else
1489  {
1490  //Trigger a flush of the entire address lookup table
1494  }
1495 }
1496 
1497 
1498 /**
1499  * @brief Set forward ports for unknown multicast packets
1500  * @param[in] interface Underlying network interface
1501  * @param[in] enable Enable or disable forwarding of unknown multicast packets
1502  * @param[in] forwardPorts Port map
1503  **/
1504 
1506  bool_t enable, uint32_t forwardPorts)
1507 {
1508  uint32_t temp;
1509 
1510  //Read Unknown Multicast Control register
1512 
1513  //Clear port map
1515 
1516  //Enable or disable forwarding of unknown multicast packets
1517  if(enable)
1518  {
1519  //Enable forwarding
1521 
1522  //Check whether unknown multicast packets should be forwarded to the CPU port
1523  if((forwardPorts & SWITCH_CPU_PORT_MASK) != 0)
1524  {
1526  }
1527 
1528  //Select the desired forward ports
1529  temp |= forwardPorts & KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP_ALL;
1530  }
1531  else
1532  {
1533  //Disable forwarding
1535  }
1536 
1537  //Write the value back to Unknown Multicast Control register
1539 }
1540 
1541 
1542 /**
1543  * @brief Write PHY register
1544  * @param[in] interface Underlying network interface
1545  * @param[in] port Port number
1546  * @param[in] address PHY register address
1547  * @param[in] data Register value
1548  **/
1549 
1550 void ksz9896WritePhyReg(NetInterface *interface, uint8_t port,
1551  uint8_t address, uint16_t data)
1552 {
1553  uint16_t n;
1554 
1555  //SPI slave mode?
1556  if(interface->spiDriver != NULL)
1557  {
1558  //The SPI interface provides access to all PHY registers
1560  //Write the 16-bit value
1561  ksz9896WriteSwitchReg16(interface, n, data);
1562  }
1563  else if(interface->smiDriver != NULL)
1564  {
1565  //Write the specified PHY register
1566  interface->smiDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1567  }
1568  else
1569  {
1570  //Write the specified PHY register
1571  interface->nicDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1572  }
1573 }
1574 
1575 
1576 /**
1577  * @brief Read PHY register
1578  * @param[in] interface Underlying network interface
1579  * @param[in] port Port number
1580  * @param[in] address PHY register address
1581  * @return Register value
1582  **/
1583 
1584 uint16_t ksz9896ReadPhyReg(NetInterface *interface, uint8_t port,
1585  uint8_t address)
1586 {
1587  uint16_t n;
1588  uint16_t data;
1589 
1590  //SPI slave mode?
1591  if(interface->spiDriver != NULL)
1592  {
1593  //The SPI interface provides access to all PHY registers
1595  //Read the 16-bit value
1596  data = ksz9896ReadSwitchReg16(interface, n);
1597  }
1598  else if(interface->smiDriver != NULL)
1599  {
1600  //Read the specified PHY register
1601  data = interface->smiDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1602  }
1603  else
1604  {
1605  //Read the specified PHY register
1606  data = interface->nicDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1607  }
1608 
1609  //Return register value
1610  return data;
1611 }
1612 
1613 
1614 /**
1615  * @brief Dump PHY registers for debugging purpose
1616  * @param[in] interface Underlying network interface
1617  * @param[in] port Port number
1618  **/
1619 
1620 void ksz9896DumpPhyReg(NetInterface *interface, uint8_t port)
1621 {
1622  uint8_t i;
1623 
1624  //Loop through PHY registers
1625  for(i = 0; i < 32; i++)
1626  {
1627  //Display current PHY register
1628  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
1629  ksz9896ReadPhyReg(interface, port, i));
1630  }
1631 
1632  //Terminate with a line feed
1633  TRACE_DEBUG("\r\n");
1634 }
1635 
1636 
1637 /**
1638  * @brief Write MMD register
1639  * @param[in] interface Underlying network interface
1640  * @param[in] port Port number
1641  * @param[in] devAddr Device address
1642  * @param[in] regAddr Register address
1643  * @param[in] data Register value
1644  **/
1645 
1646 void ksz9896WriteMmdReg(NetInterface *interface, uint8_t port,
1647  uint8_t devAddr, uint16_t regAddr, uint16_t data)
1648 {
1649  //Select register operation
1652 
1653  //Write MMD register address
1655 
1656  //Select data operation
1659 
1660  //Write the content of the MMD register
1662 }
1663 
1664 
1665 /**
1666  * @brief Read MMD register
1667  * @param[in] interface Underlying network interface
1668  * @param[in] port Port number
1669  * @param[in] devAddr Device address
1670  * @param[in] regAddr Register address
1671  * @return Register value
1672  **/
1673 
1674 uint16_t ksz9896ReadMmdReg(NetInterface *interface, uint8_t port,
1675  uint8_t devAddr, uint16_t regAddr)
1676 {
1677  //Select register operation
1680 
1681  //Write MMD register address
1683 
1684  //Select data operation
1687 
1688  //Read the content of the MMD register
1689  return ksz9896ReadPhyReg(interface, port, KSZ9896_MMDAADR);
1690 }
1691 
1692 
1693 /**
1694  * @brief Write switch register (8 bits)
1695  * @param[in] interface Underlying network interface
1696  * @param[in] address Switch register address
1697  * @param[in] data Register value
1698  **/
1699 
1700 void ksz9896WriteSwitchReg8(NetInterface *interface, uint16_t address,
1701  uint8_t data)
1702 {
1703  uint32_t command;
1704 
1705  //SPI slave mode?
1706  if(interface->spiDriver != NULL)
1707  {
1708  //Set up a write operation
1709  command = KSZ9896_SPI_CMD_WRITE;
1710  //Set register address
1711  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1712 
1713  //Pull the CS pin low
1714  interface->spiDriver->assertCs();
1715 
1716  //Write 32-bit command
1717  interface->spiDriver->transfer((command >> 24) & 0xFF);
1718  interface->spiDriver->transfer((command >> 16) & 0xFF);
1719  interface->spiDriver->transfer((command >> 8) & 0xFF);
1720  interface->spiDriver->transfer(command & 0xFF);
1721 
1722  //Write 8-bit data
1723  interface->spiDriver->transfer(data);
1724 
1725  //Terminate the operation by raising the CS pin
1726  interface->spiDriver->deassertCs();
1727  }
1728  else
1729  {
1730  //The MDC/MDIO interface does not have access to all the configuration
1731  //registers. It can only access the standard MIIM registers
1732  }
1733 }
1734 
1735 
1736 /**
1737  * @brief Read switch register (8 bits)
1738  * @param[in] interface Underlying network interface
1739  * @param[in] address Switch register address
1740  * @return Register value
1741  **/
1742 
1743 uint8_t ksz9896ReadSwitchReg8(NetInterface *interface, uint16_t address)
1744 {
1745  uint8_t data;
1746  uint32_t command;
1747 
1748  //SPI slave mode?
1749  if(interface->spiDriver != NULL)
1750  {
1751  //Set up a read operation
1752  command = KSZ9896_SPI_CMD_READ;
1753  //Set register address
1754  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1755 
1756  //Pull the CS pin low
1757  interface->spiDriver->assertCs();
1758 
1759  //Write 32-bit command
1760  interface->spiDriver->transfer((command >> 24) & 0xFF);
1761  interface->spiDriver->transfer((command >> 16) & 0xFF);
1762  interface->spiDriver->transfer((command >> 8) & 0xFF);
1763  interface->spiDriver->transfer(command & 0xFF);
1764 
1765  //Read 8-bit data
1766  data = interface->spiDriver->transfer(0xFF);
1767 
1768  //Terminate the operation by raising the CS pin
1769  interface->spiDriver->deassertCs();
1770  }
1771  else
1772  {
1773  //The MDC/MDIO interface does not have access to all the configuration
1774  //registers. It can only access the standard MIIM registers
1775  data = 0;
1776  }
1777 
1778  //Return register value
1779  return data;
1780 }
1781 
1782 
1783 /**
1784  * @brief Write switch register (16 bits)
1785  * @param[in] interface Underlying network interface
1786  * @param[in] address Switch register address
1787  * @param[in] data Register value
1788  **/
1789 
1791  uint16_t data)
1792 {
1793  uint32_t command;
1794 
1795  //SPI slave mode?
1796  if(interface->spiDriver != NULL)
1797  {
1798  //Set up a write operation
1799  command = KSZ9896_SPI_CMD_WRITE;
1800  //Set register address
1801  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1802 
1803  //Pull the CS pin low
1804  interface->spiDriver->assertCs();
1805 
1806  //Write 32-bit command
1807  interface->spiDriver->transfer((command >> 24) & 0xFF);
1808  interface->spiDriver->transfer((command >> 16) & 0xFF);
1809  interface->spiDriver->transfer((command >> 8) & 0xFF);
1810  interface->spiDriver->transfer(command & 0xFF);
1811 
1812  //Write 16-bit data
1813  interface->spiDriver->transfer((data >> 8) & 0xFF);
1814  interface->spiDriver->transfer(data & 0xFF);
1815 
1816  //Terminate the operation by raising the CS pin
1817  interface->spiDriver->deassertCs();
1818  }
1819  else
1820  {
1821  //The MDC/MDIO interface does not have access to all the configuration
1822  //registers. It can only access the standard MIIM registers
1823  }
1824 }
1825 
1826 
1827 /**
1828  * @brief Read switch register (16 bits)
1829  * @param[in] interface Underlying network interface
1830  * @param[in] address Switch register address
1831  * @return Register value
1832  **/
1833 
1834 uint16_t ksz9896ReadSwitchReg16(NetInterface *interface, uint16_t address)
1835 {
1836  uint16_t data;
1837  uint32_t command;
1838 
1839  //SPI slave mode?
1840  if(interface->spiDriver != NULL)
1841  {
1842  //Set up a read operation
1843  command = KSZ9896_SPI_CMD_READ;
1844  //Set register address
1845  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1846 
1847  //Pull the CS pin low
1848  interface->spiDriver->assertCs();
1849 
1850  //Write 32-bit command
1851  interface->spiDriver->transfer((command >> 24) & 0xFF);
1852  interface->spiDriver->transfer((command >> 16) & 0xFF);
1853  interface->spiDriver->transfer((command >> 8) & 0xFF);
1854  interface->spiDriver->transfer(command & 0xFF);
1855 
1856  //Read 16-bit data
1857  data = interface->spiDriver->transfer(0xFF) << 8;
1858  data |= interface->spiDriver->transfer(0xFF);
1859 
1860  //Terminate the operation by raising the CS pin
1861  interface->spiDriver->deassertCs();
1862  }
1863  else
1864  {
1865  //The MDC/MDIO interface does not have access to all the configuration
1866  //registers. It can only access the standard MIIM registers
1867  data = 0;
1868  }
1869 
1870  //Return register value
1871  return data;
1872 }
1873 
1874 
1875 /**
1876  * @brief Write switch register (32 bits)
1877  * @param[in] interface Underlying network interface
1878  * @param[in] address Switch register address
1879  * @param[in] data Register value
1880  **/
1881 
1883  uint32_t data)
1884 {
1885  uint32_t command;
1886 
1887  //SPI slave mode?
1888  if(interface->spiDriver != NULL)
1889  {
1890  //Set up a write operation
1891  command = KSZ9896_SPI_CMD_WRITE;
1892  //Set register address
1893  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1894 
1895  //Pull the CS pin low
1896  interface->spiDriver->assertCs();
1897 
1898  //Write 32-bit command
1899  interface->spiDriver->transfer((command >> 24) & 0xFF);
1900  interface->spiDriver->transfer((command >> 16) & 0xFF);
1901  interface->spiDriver->transfer((command >> 8) & 0xFF);
1902  interface->spiDriver->transfer(command & 0xFF);
1903 
1904  //Write 32-bit data
1905  interface->spiDriver->transfer((data >> 24) & 0xFF);
1906  interface->spiDriver->transfer((data >> 16) & 0xFF);
1907  interface->spiDriver->transfer((data >> 8) & 0xFF);
1908  interface->spiDriver->transfer(data & 0xFF);
1909 
1910  //Terminate the operation by raising the CS pin
1911  interface->spiDriver->deassertCs();
1912  }
1913  else
1914  {
1915  //The MDC/MDIO interface does not have access to all the configuration
1916  //registers. It can only access the standard MIIM registers
1917  }
1918 }
1919 
1920 
1921 /**
1922  * @brief Read switch register (32 bits)
1923  * @param[in] interface Underlying network interface
1924  * @param[in] address Switch register address
1925  * @return Register value
1926  **/
1927 
1928 uint32_t ksz9896ReadSwitchReg32(NetInterface *interface, uint16_t address)
1929 {
1930  uint32_t data;
1931  uint32_t command;
1932 
1933  //SPI slave mode?
1934  if(interface->spiDriver != NULL)
1935  {
1936  //Set up a read operation
1937  command = KSZ9896_SPI_CMD_READ;
1938  //Set register address
1939  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1940 
1941  //Pull the CS pin low
1942  interface->spiDriver->assertCs();
1943 
1944  //Write 32-bit command
1945  interface->spiDriver->transfer((command >> 24) & 0xFF);
1946  interface->spiDriver->transfer((command >> 16) & 0xFF);
1947  interface->spiDriver->transfer((command >> 8) & 0xFF);
1948  interface->spiDriver->transfer(command & 0xFF);
1949 
1950  //Read 32-bit data
1951  data = interface->spiDriver->transfer(0xFF) << 24;
1952  data |= interface->spiDriver->transfer(0xFF) << 16;
1953  data |= interface->spiDriver->transfer(0xFF) << 8;
1954  data |= interface->spiDriver->transfer(0xFF);
1955 
1956  //Terminate the operation by raising the CS pin
1957  interface->spiDriver->deassertCs();
1958  }
1959  else
1960  {
1961  //The MDC/MDIO interface does not have access to all the configuration
1962  //registers. It can only access the standard MIIM registers
1963  data = 0;
1964  }
1965 
1966  //Return register value
1967  return data;
1968 }
uint8_t type
Definition: coap_common.h:176
unsigned int uint_t
Definition: compiler_port.h:50
int bool_t
Definition: compiler_port.h:53
#define HTONS(value)
Definition: cpu_endian.h:410
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t n
uint16_t port
Definition: dns_common.h:267
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_PORT
Definition: error.h:104
@ ERROR_NOT_FOUND
Definition: error.h:147
@ ERROR_END_OF_TABLE
Definition: error.h:290
@ ERROR_INVALID_ENTRY
Definition: error.h:288
@ ERROR_TABLE_FULL
Definition: error.h:289
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_INVALID_LENGTH
Definition: error.h:111
uint8_t data[]
Definition: ethernet.h:222
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
EthHeader
Definition: ethernet.h:223
error_t ethPadFrame(NetBuffer *buffer, size_t *length)
Ethernet frame padding.
Helper functions for Ethernet.
Ipv6Addr address[]
Definition: ipv6.h:316
void ksz9896EnableMldSnooping(NetInterface *interface, bool_t enable)
Enable MLD snooping.
uint16_t ksz9896ReadPhyReg(NetInterface *interface, uint8_t port, uint8_t address)
Read PHY register.
uint32_t ksz9896ReadSwitchReg32(NetInterface *interface, uint16_t address)
Read switch register (32 bits)
void ksz9896WriteSwitchReg32(NetInterface *interface, uint16_t address, uint32_t data)
Write switch register (32 bits)
void ksz9896EventHandler(NetInterface *interface)
KSZ9896 event handler.
uint8_t ksz9896ReadSwitchReg8(NetInterface *interface, uint16_t address)
Read switch register (8 bits)
error_t ksz9896UntagFrame(NetInterface *interface, uint8_t **frame, size_t *length, NetRxAncillary *ancillary)
Decode tail tag from incoming Ethernet frame.
const uint16_t ksz9896IngressTailTag[6]
Tail tag rules (host to KSZ9896)
NicDuplexMode ksz9896GetDuplexMode(NetInterface *interface, uint8_t port)
Get duplex mode.
error_t ksz9896GetDynamicFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the dynamic MAC table.
void ksz9896FlushDynamicFdbTable(NetInterface *interface, uint8_t port)
Flush dynamic MAC table.
error_t ksz9896Init(NetInterface *interface)
KSZ9896 Ethernet switch initialization.
bool_t ksz9896GetLinkState(NetInterface *interface, uint8_t port)
Get link state.
void ksz9896Tick(NetInterface *interface)
KSZ9896 timer handler.
void ksz9896EnableIgmpSnooping(NetInterface *interface, bool_t enable)
Enable IGMP snooping.
error_t ksz9896TagFrame(NetInterface *interface, NetBuffer *buffer, size_t *offset, NetTxAncillary *ancillary)
Add tail tag to Ethernet frame.
__weak_func void ksz9896InitHook(NetInterface *interface)
KSZ9896 custom configuration.
void ksz9896EnableIrq(NetInterface *interface)
Enable interrupts.
uint32_t ksz9896GetLinkSpeed(NetInterface *interface, uint8_t port)
Get link speed.
error_t ksz9896DeleteStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Remove an entry from the static MAC table.
void ksz9896DumpPhyReg(NetInterface *interface, uint8_t port)
Dump PHY registers for debugging purpose.
void ksz9896SetAgingTime(NetInterface *interface, uint32_t agingTime)
Set aging time for dynamic filtering entries.
void ksz9896FlushStaticFdbTable(NetInterface *interface)
Flush static MAC table.
void ksz9896SetPortState(NetInterface *interface, uint8_t port, SwitchPortState state)
Set port state.
void ksz9896EnableRsvdMcastTable(NetInterface *interface, bool_t enable)
Enable reserved multicast table.
const SwitchDriver ksz9896SwitchDriver
KSZ9896 Ethernet switch driver.
void ksz9896WriteMmdReg(NetInterface *interface, uint8_t port, uint8_t devAddr, uint16_t regAddr, uint16_t data)
Write MMD register.
void ksz9896WriteSwitchReg8(NetInterface *interface, uint16_t address, uint8_t data)
Write switch register (8 bits)
error_t ksz9896GetStaticFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the static MAC table.
SwitchPortState ksz9896GetPortState(NetInterface *interface, uint8_t port)
Get port state.
error_t ksz9896AddStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Add a new entry to the static MAC table.
uint16_t ksz9896ReadMmdReg(NetInterface *interface, uint8_t port, uint8_t devAddr, uint16_t regAddr)
Read MMD register.
uint16_t ksz9896ReadSwitchReg16(NetInterface *interface, uint16_t address)
Read switch register (16 bits)
void ksz9896DisableIrq(NetInterface *interface)
Disable interrupts.
void ksz9896SetUnknownMcastFwdPorts(NetInterface *interface, bool_t enable, uint32_t forwardPorts)
Set forward ports for unknown multicast packets.
void ksz9896WriteSwitchReg16(NetInterface *interface, uint16_t address, uint16_t data)
Write switch register (16 bits)
void ksz9896WritePhyReg(NetInterface *interface, uint8_t port, uint8_t address, uint16_t data)
Write PHY register.
KSZ9896 6-port Gigabit Ethernet switch driver.
#define KSZ9896_ALU_TABLE_ENTRY2_PORT_FORWARD
#define KSZ9896_PHYCON_DUPLEX_STATUS
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_TABLE_SELECT
#define KSZ9896_SWITCH_LUE_CTRL1_FLUSH_ALU_TABLE
#define KSZ9896_PHYCON_SPEED_100BTX
#define KSZ9896_SWITCH_LUE_CTRL0_HASH_OPTION_CRC
#define KSZ9896_PORTn_ETH_PHY_REG(port, addr)
#define KSZ9896_PORT6_XMII_CTRL1_IF_TYPE_RGMII
#define KSZ9896_MMDACR_FUNC_DATA_NO_POST_INC
#define KSZ9896_ALU_TABLE_CTRL_VALID_ENTRY_OR_SEARCH_END
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP_PORT6
#define KSZ9896_ALU_TABLE_CTRL_START_FINISH
#define KSZ9896_SPI_CMD_ADDR
#define KSZ9896_PORT6_MASK
#define KSZ9896_STATIC_MAC_TABLE_SIZE
#define KSZ9896_PORT6_XMII_CTRL1_SPEED_1000
#define KSZ9896_UNKONWN_MULTICAST_CTRL
#define KSZ9896_ALU_TABLE_CTRL_VALID
#define KSZ9896_PORT6_XMII_CTRL1_IF_TYPE
#define KSZ9896_GLOBAL_PORT_MIRROR_SNOOP_CTRL_IGMP_SNOOP_EN
#define KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN
#define KSZ9896_SWITCH_OP
#define KSZ9896_CHIP_ID1_DEFAULT
#define KSZ9896_STATIC_TABLE_ENTRY4
#define KSZ9896_TAIL_TAG_DEST_PORT4
#define KSZ9896_STATIC_TABLE_ENTRY2
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_ACTION
#define KSZ9896_ALU_TABLE_ENTRY2_PORT4_FORWARD
#define KSZ9896_ALU_TABLE_ENTRY2_PORT2_FORWARD
#define KSZ9896_PORTn_MSTP_STATE_LEARNING_DIS
#define KSZ9896_SWITCH_LUE_CTRL0_AGE_COUNT_DEFAULT
#define KSZ9896_SPI_CMD_READ
#define KSZ9896_TAIL_TAG_PORT_BLOCKING_OVERRIDE
#define KSZ9896_MMD_EEE_ADV
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_TABLE_INDEX
#define KSZ9896_ALU_TABLE_ENTRY1
#define KSZ9896_TAIL_TAG_SRC_PORT
#define KSZ9896_MMDACR_FUNC_ADDR
#define KSZ9896_GLOBAL_PORT_MIRROR_SNOOP_CTRL
#define KSZ9896_ALU_TABLE_ENTRY2_PORT6_FORWARD
#define KSZ9896_SWITCH_LUE_CTRL0
#define KSZ9896_SWITCH_LUE_CTRL0_RESERVED_MCAST_LOOKUP_EN
#define KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN
#define KSZ9896_SWITCH_LUE_CTRL2_FLUSH_OPTION
#define KSZ9896_TAIL_TAG_DEST_PORT5
#define KSZ9896_SWITCH_MAC_CTRL0
#define KSZ9896_PORT6_XMII_CTRL1_RGMII_ID_EG
#define KSZ9896_PORTn_MSTP_STATE(port)
#define KSZ9896_PORT6_XMII_CTRL0_DUPLEX
#define KSZ9896_SWITCH_LUE_CTRL3_AGE_PERIOD_DEFAULT
#define KSZ9896_STATIC_TABLE_ENTRY1
#define KSZ9896_SPI_CMD_WRITE
#define KSZ9896_PHYCON
#define KSZ9896_BMSR_LINK_STATUS
#define KSZ9896_PORT6_OP_CTRL0
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP_ALL
#define KSZ9896_PORT2
#define KSZ9896_MMD_LED_MODE_RESERVED_DEFAULT
#define KSZ9896_PORT5
#define KSZ9896_SWITCH_OP_START_SWITCH
#define KSZ9896_CHIP_ID1
#define KSZ9896_ALU_TABLE_ENTRY4
#define KSZ9896_PORT6
#define KSZ9896_ALU_TABLE_ENTRY3
#define KSZ9896_TAIL_TAG_DEST_PORT2
#define KSZ9896_ALU_TABLE_ENTRY2_PORT5_FORWARD
#define KSZ9896_SWITCH_LUE_CTRL3
#define KSZ9896_TAIL_TAG_DEST_PORT3
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD
#define KSZ9896_PORT_MASK
#define KSZ9896_PORT6_XMII_CTRL1_RGMII_ID_IG
#define KSZ9896_TAIL_TAG_NORMAL_ADDR_LOOKUP
#define KSZ9896_BMSR
#define KSZ9896_STATIC_TABLE_ENTRY3
#define KSZ9896_STATIC_TABLE_ENTRY2_OVERRIDE
#define KSZ9896_PHYCON_SPEED_1000BT
#define KSZ9896_PORTn_OP_CTRL0_TAIL_TAG_EN
#define KSZ9896_PORT6_XMII_CTRL1
#define KSZ9896_STATIC_TABLE_ENTRY2_PORT_FORWARD
#define KSZ9896_SWITCH_LUE_CTRL1
#define KSZ9896_ALU_TABLE_ENTRY2_PORT1_FORWARD
#define KSZ9896_SWITCH_MAC_CTRL0_FRAME_LEN_CHECK_EN
#define KSZ9896_PORT6_XMII_CTRL0
#define KSZ9896_GLOBAL_PORT_MIRROR_SNOOP_CTRL_MLD_SNOOP_EN
#define KSZ9896_PORT1
#define KSZ9896_SWITCH_LUE_CTRL2
#define KSZ9896_MMD_LED_MODE_LED_MODE_TRI_COLOR_DUAL
#define KSZ9896_MMD_LED_MODE
#define KSZ9896_ALU_TABLE_CTRL_ACTION_SEARCH
#define KSZ9896_PHYCON_SPEED_10BT
#define KSZ9896_STATIC_MCAST_TABLE_CTRL
#define KSZ9896_SWITCH_LUE_CTRL1_FLUSH_MSTP_ENTRIES
#define KSZ9896_MMDACR_DEVAD
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_START_FINISH
#define KSZ9896_STATIC_TABLE_ENTRY1_VALID
#define KSZ9896_PORT6_XMII_CTRL0_SPEED_10_100
#define KSZ9896_SWITCH_LUE_CTRL2_FLUSH_OPTION_DYNAMIC
#define KSZ9896_ALU_TABLE_ENTRY2
#define KSZ9896_PORT4
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP
#define KSZ9896_MMDAADR
#define KSZ9896_ALU_TABLE_CTRL
#define KSZ9896_PORT3
#define KSZ9896_TAIL_TAG_DEST_PORT1
#define KSZ9896_ALU_TABLE_ENTRY2_PORT3_FORWARD
#define KSZ9896_MMDACR
uint16_t regAddr
TCP/IP stack core.
#define NET_INTERFACE_COUNT
Definition: net.h:113
#define NetInterface
Definition: net.h:36
#define netInterface
Definition: net_legacy.h:199
#define netEvent
Definition: net_legacy.h:196
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:588
#define NetRxAncillary
Definition: net_misc.h:40
#define NetTxAncillary
Definition: net_misc.h:36
void nicNotifyLinkChange(NetInterface *interface)
Process link state change notification.
Definition: nic.c:548
#define SMI_OPCODE_WRITE
Definition: nic.h:66
#define SMI_OPCODE_READ
Definition: nic.h:67
NicDuplexMode
Duplex mode.
Definition: nic.h:122
@ NIC_FULL_DUPLEX_MODE
Definition: nic.h:125
@ NIC_HALF_DUPLEX_MODE
Definition: nic.h:124
@ NIC_UNKNOWN_DUPLEX_MODE
Definition: nic.h:123
#define SWITCH_CPU_PORT_MASK
Definition: nic.h:60
SwitchPortState
Switch port state.
Definition: nic.h:134
@ SWITCH_PORT_STATE_UNKNOWN
Definition: nic.h:135
@ SWITCH_PORT_STATE_FORWARDING
Definition: nic.h:140
@ SWITCH_PORT_STATE_LISTENING
Definition: nic.h:138
@ SWITCH_PORT_STATE_DISABLED
Definition: nic.h:136
@ SWITCH_PORT_STATE_LEARNING
Definition: nic.h:139
@ NIC_LINK_SPEED_100MBPS
Definition: nic.h:112
@ NIC_LINK_SPEED_10MBPS
Definition: nic.h:111
@ NIC_LINK_SPEED_UNKNOWN
Definition: nic.h:110
@ NIC_LINK_SPEED_1GBPS
Definition: nic.h:113
#define MIN(a, b)
Definition: os_port.h:63
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
Ethernet switch driver.
Definition: nic.h:322
Forwarding database entry.
Definition: nic.h:149
MacAddr macAddr
Definition: nic.h:150
uint32_t destPorts
Definition: nic.h:152
bool_t override
Definition: nic.h:153
uint8_t srcPort
Definition: nic.h:151
uint8_t length
Definition: tcp.h:368
uint8_t value[]
Definition: tcp.h:369