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-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 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
236  osSetEvent(&interface->netContext->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 __weak_func 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  NetContext *context;
269  NetInterface *virtualInterface;
270 
271  //Point to the TCP/IP stack context
272  context = interface->netContext;
273 
274  //Loop through network interfaces
275  for(i = 0; i < context->numInterfaces; i++)
276  {
277  //Point to the current interface
278  virtualInterface = &context->interfaces[i];
279 
280  //Check whether the current virtual interface is attached to the
281  //physical interface
282  if(virtualInterface == interface ||
283  virtualInterface->parent == interface)
284  {
285  //Retrieve current link state
286  linkState = ksz9896GetLinkState(interface, virtualInterface->port);
287 
288  //Link up or link down event?
289  if(linkState != virtualInterface->linkState)
290  {
291  //Set event flag
292  interface->phyEvent = TRUE;
293  //Notify the TCP/IP stack of the event
294  osSetEvent(&interface->netContext->event);
295  }
296  }
297  }
298  }
299  else
300 #endif
301  {
302  //Initialize link state
303  linkState = FALSE;
304 
305  //Loop through the ports
307  {
308  //Retrieve current link state
309  if(ksz9896GetLinkState(interface, port))
310  {
311  linkState = TRUE;
312  }
313  }
314 
315  //Link up or link down event?
316  if(linkState != interface->linkState)
317  {
318  //Set event flag
319  interface->phyEvent = TRUE;
320  //Notify the TCP/IP stack of the event
321  osSetEvent(&interface->netContext->event);
322  }
323  }
324 }
325 
326 
327 /**
328  * @brief Enable interrupts
329  * @param[in] interface Underlying network interface
330  **/
331 
333 {
334 }
335 
336 
337 /**
338  * @brief Disable interrupts
339  * @param[in] interface Underlying network interface
340  **/
341 
343 {
344 }
345 
346 
347 /**
348  * @brief KSZ9896 event handler
349  * @param[in] interface Underlying network interface
350  **/
351 
352 __weak_func void ksz9896EventHandler(NetInterface *interface)
353 {
354  uint_t port;
355  bool_t linkState;
356 
357 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
358  //Port separation mode?
359  if(interface->port != 0)
360  {
361  uint_t i;
362  NetContext *context;
363  NetInterface *virtualInterface;
364 
365  //Point to the TCP/IP stack context
366  context = interface->netContext;
367 
368  //Loop through network interfaces
369  for(i = 0; i < context->numInterfaces; i++)
370  {
371  //Point to the current interface
372  virtualInterface = &context->interfaces[i];
373 
374  //Check whether the current virtual interface is attached to the
375  //physical interface
376  if(virtualInterface == interface ||
377  virtualInterface->parent == interface)
378  {
379  //Get the port number associated with the current interface
380  port = virtualInterface->port;
381 
382  //Valid port?
383  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
384  {
385  //Retrieve current link state
386  linkState = ksz9896GetLinkState(interface, port);
387 
388  //Link up event?
389  if(linkState && !virtualInterface->linkState)
390  {
391  //Retrieve host interface speed
392  interface->linkSpeed = ksz9896GetLinkSpeed(interface,
393  KSZ9896_PORT6);
394 
395  //Retrieve host interface duplex mode
396  interface->duplexMode = ksz9896GetDuplexMode(interface,
397  KSZ9896_PORT6);
398 
399  //Adjust MAC configuration parameters for proper operation
400  interface->nicDriver->updateMacConfig(interface);
401 
402  //Check current speed
403  virtualInterface->linkSpeed = ksz9896GetLinkSpeed(interface,
404  port);
405 
406  //Check current duplex mode
407  virtualInterface->duplexMode = ksz9896GetDuplexMode(interface,
408  port);
409 
410  //Update link state
411  virtualInterface->linkState = TRUE;
412 
413  //Process link state change event
414  nicNotifyLinkChange(virtualInterface);
415  }
416  //Link down event
417  else if(!linkState && virtualInterface->linkState)
418  {
419  //Update link state
420  virtualInterface->linkState = FALSE;
421 
422  //Process link state change event
423  nicNotifyLinkChange(virtualInterface);
424  }
425  }
426  }
427  }
428  }
429  else
430 #endif
431  {
432  //Initialize link state
433  linkState = FALSE;
434 
435  //Loop through the ports
437  {
438  //Retrieve current link state
439  if(ksz9896GetLinkState(interface, port))
440  {
441  linkState = TRUE;
442  }
443  }
444 
445  //Link up event?
446  if(linkState)
447  {
448  //Retrieve host interface speed
449  interface->linkSpeed = ksz9896GetLinkSpeed(interface, KSZ9896_PORT6);
450  //Retrieve host interface duplex mode
451  interface->duplexMode = ksz9896GetDuplexMode(interface, KSZ9896_PORT6);
452 
453  //Adjust MAC configuration parameters for proper operation
454  interface->nicDriver->updateMacConfig(interface);
455 
456  //Update link state
457  interface->linkState = TRUE;
458  }
459  else
460  {
461  //Update link state
462  interface->linkState = FALSE;
463  }
464 
465  //Process link state change event
466  nicNotifyLinkChange(interface);
467  }
468 }
469 
470 
471 /**
472  * @brief Add tail tag to Ethernet frame
473  * @param[in] interface Underlying network interface
474  * @param[in] buffer Multi-part buffer containing the payload
475  * @param[in,out] offset Offset to the first payload byte
476  * @param[in] ancillary Additional options passed to the stack along with
477  * the packet
478  * @return Error code
479  **/
480 
482  size_t *offset, NetTxAncillary *ancillary)
483 {
484  error_t error;
485 
486  //Initialize status code
487  error = NO_ERROR;
488 
489 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
490  //SPI slave mode?
491  if(interface->spiDriver != NULL)
492  {
493  //Valid port?
494  if(ancillary->port <= KSZ9896_PORT5)
495  {
496  size_t length;
497  const uint16_t *tailTag;
498 
499  //The two-byte tail tagging is used to indicate the destination port
500  tailTag = &ksz9896IngressTailTag[ancillary->port];
501 
502  //Retrieve the length of the Ethernet frame
503  length = netBufferGetLength(buffer) - *offset;
504 
505  //The host controller should manually add padding to the packet before
506  //inserting the tail tag
507  error = ethPadFrame(buffer, &length);
508 
509  //Check status code
510  if(!error)
511  {
512  //The tail tag is inserted at the end of the packet, just before
513  //the CRC
514  error = netBufferAppend(buffer, tailTag, sizeof(uint16_t));
515  }
516  }
517  else
518  {
519  //The port number is not valid
520  error = ERROR_INVALID_PORT;
521  }
522  }
523 #endif
524 
525  //Return status code
526  return error;
527 }
528 
529 
530 /**
531  * @brief Decode tail tag from incoming Ethernet frame
532  * @param[in] interface Underlying network interface
533  * @param[in,out] frame Pointer to the received Ethernet frame
534  * @param[in,out] length Length of the frame, in bytes
535  * @param[in,out] ancillary Additional options passed to the stack along with
536  * the packet
537  * @return Error code
538  **/
539 
540 error_t ksz9896UntagFrame(NetInterface *interface, uint8_t **frame,
541  size_t *length, NetRxAncillary *ancillary)
542 {
543  error_t error;
544 
545  //Initialize status code
546  error = NO_ERROR;
547 
548 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
549  //SPI slave mode?
550  if(interface->spiDriver != NULL)
551  {
552  //Valid Ethernet frame received?
553  if(*length >= (sizeof(EthHeader) + sizeof(uint8_t)))
554  {
555  uint8_t *tailTag;
556 
557  //The tail tag is inserted at the end of the packet, just before
558  //the CRC
559  tailTag = *frame + *length - sizeof(uint8_t);
560 
561  //The one byte tail tagging is used to indicate the source port
562  ancillary->port = (*tailTag & KSZ9896_TAIL_TAG_SRC_PORT) + 1;
563 
564  //Strip tail tag from Ethernet frame
565  *length -= sizeof(uint8_t);
566  }
567  else
568  {
569  //Drop the received frame
570  error = ERROR_INVALID_LENGTH;
571  }
572  }
573  else
574  {
575  //Tail tagging mode cannot be enabled through MDC/MDIO interface
576  ancillary->port = 0;
577  }
578 #endif
579 
580  //Return status code
581  return error;
582 }
583 
584 
585 /**
586  * @brief Get link state
587  * @param[in] interface Underlying network interface
588  * @param[in] port Port number
589  * @return Link state
590  **/
591 
593 {
594  uint16_t value;
595  bool_t linkState;
596 
597  //Check port number
598  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
599  {
600  //Any link failure condition is latched in the BMSR register. Reading
601  //the register twice will always return the actual link status
602  value = ksz9896ReadPhyReg(interface, port, KSZ9896_BMSR);
603  value = ksz9896ReadPhyReg(interface, port, KSZ9896_BMSR);
604 
605  //Retrieve current link state
606  linkState = (value & KSZ9896_BMSR_LINK_STATUS) ? TRUE : FALSE;
607  }
608  else
609  {
610  //The specified port number is not valid
611  linkState = FALSE;
612  }
613 
614  //Return link status
615  return linkState;
616 }
617 
618 
619 /**
620  * @brief Get link speed
621  * @param[in] interface Underlying network interface
622  * @param[in] port Port number
623  * @return Link speed
624  **/
625 
626 uint32_t ksz9896GetLinkSpeed(NetInterface *interface, uint8_t port)
627 {
628  uint8_t type;
629  uint16_t value;
630  uint32_t linkSpeed;
631 
632  //Check port number
633  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
634  {
635  //Read PHY control register
637 
638  //Retrieve current link speed
640  {
641  //1000BASE-T
642  linkSpeed = NIC_LINK_SPEED_1GBPS;
643  }
644  else if((value & KSZ9896_PHYCON_SPEED_100BTX) != 0)
645  {
646  //100BASE-TX
647  linkSpeed = NIC_LINK_SPEED_100MBPS;
648  }
649  else if((value & KSZ9896_PHYCON_SPEED_10BT) != 0)
650  {
651  //10BASE-T
652  linkSpeed = NIC_LINK_SPEED_10MBPS;
653  }
654  else
655  {
656  //The link speed is not valid
657  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
658  }
659  }
660  else if(port == KSZ9896_PORT6)
661  {
662  //SPI slave mode?
663  if(interface->spiDriver != NULL)
664  {
665  //Read port 6 XMII control 1 register
667 
668  //Retrieve host interface type
670 
671  //Gigabit interface?
674  {
675  //1000 Mb/s mode
676  linkSpeed = NIC_LINK_SPEED_1GBPS;
677  }
678  else
679  {
680  //Read port 6 XMII control 0 register
682 
683  //Retrieve host interface speed
685  {
686  //100 Mb/s mode
687  linkSpeed = NIC_LINK_SPEED_100MBPS;
688  }
689  else
690  {
691  //10 Mb/s mode
692  linkSpeed = NIC_LINK_SPEED_10MBPS;
693  }
694  }
695  }
696  else
697  {
698  //The MDC/MDIO interface does not have access to all the configuration
699  //registers. It can only access the standard MIIM registers
700  linkSpeed = NIC_LINK_SPEED_100MBPS;
701  }
702  }
703  else
704  {
705  //The specified port number is not valid
706  linkSpeed = NIC_LINK_SPEED_UNKNOWN;
707  }
708 
709  //Return link speed
710  return linkSpeed;
711 }
712 
713 
714 /**
715  * @brief Get duplex mode
716  * @param[in] interface Underlying network interface
717  * @param[in] port Port number
718  * @return Duplex mode
719  **/
720 
722 {
723  uint16_t value;
724  NicDuplexMode duplexMode;
725 
726  //Check port number
727  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
728  {
729  //Read PHY control register
731 
732  //Retrieve current duplex mode
734  {
735  duplexMode = NIC_FULL_DUPLEX_MODE;
736  }
737  else
738  {
739  duplexMode = NIC_HALF_DUPLEX_MODE;
740  }
741  }
742  else if(port == KSZ9896_PORT6)
743  {
744  //SPI slave mode?
745  if(interface->spiDriver != NULL)
746  {
747  //Read port 6 XMII control 0 register
749 
750  //Retrieve host interface duplex mode
752  {
753  duplexMode = NIC_FULL_DUPLEX_MODE;
754  }
755  else
756  {
757  duplexMode = NIC_HALF_DUPLEX_MODE;
758  }
759  }
760  else
761  {
762  //The MDC/MDIO interface does not have access to all the configuration
763  //registers. It can only access the standard MIIM registers
764  duplexMode = NIC_FULL_DUPLEX_MODE;
765  }
766  }
767  else
768  {
769  //The specified port number is not valid
770  duplexMode = NIC_UNKNOWN_DUPLEX_MODE;
771  }
772 
773  //Return duplex mode
774  return duplexMode;
775 }
776 
777 
778 /**
779  * @brief Set port state
780  * @param[in] interface Underlying network interface
781  * @param[in] port Port number
782  * @param[in] state Port state
783  **/
784 
785 void ksz9896SetPortState(NetInterface *interface, uint8_t port,
786  SwitchPortState state)
787 {
788  uint8_t temp;
789 
790  //Check port number
791  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
792  {
793  //Read MSTP state register
795 
796  //Update port state
797  switch(state)
798  {
799  //Listening state
804  break;
805 
806  //Learning state
811  break;
812 
813  //Forwarding state
818  break;
819 
820  //Disabled state
821  default:
825  break;
826  }
827 
828  //Write the value back to MSTP state register
830  }
831 }
832 
833 
834 /**
835  * @brief Get port state
836  * @param[in] interface Underlying network interface
837  * @param[in] port Port number
838  * @return Port state
839  **/
840 
842 {
843  uint8_t temp;
844  SwitchPortState state;
845 
846  //Check port number
847  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT5)
848  {
849  //Read MSTP state register
851 
852  //Check port state
853  if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
854  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) == 0 &&
856  {
857  //Disabled state
859  }
860  else if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
861  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) != 0 &&
863  {
864  //Listening state
866  }
867  else if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) == 0 &&
868  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) == 0 &&
870  {
871  //Learning state
873  }
874  else if((temp & KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN) != 0 &&
875  (temp & KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN) != 0 &&
877  {
878  //Forwarding state
880  }
881  else
882  {
883  //Unknown state
885  }
886  }
887  else
888  {
889  //The specified port number is not valid
891  }
892 
893  //Return port state
894  return state;
895 }
896 
897 
898 /**
899  * @brief Set aging time for dynamic filtering entries
900  * @param[in] interface Underlying network interface
901  * @param[in] agingTime Aging time, in seconds
902  **/
903 
904 void ksz9896SetAgingTime(NetInterface *interface, uint32_t agingTime)
905 {
906  //The Age Period in combination with the Age Count field determines the
907  //aging time of dynamic entries in the address lookup table
908  agingTime = (agingTime + 3) / 4;
909 
910  //Limit the range of the parameter
911  agingTime = MIN(agingTime, 255);
912 
913  //Write the value to Switch Lookup Engine Control 3 register
915  (uint8_t) agingTime);
916 }
917 
918 
919 /**
920  * @brief Enable IGMP snooping
921  * @param[in] interface Underlying network interface
922  * @param[in] enable Enable or disable IGMP snooping
923  **/
924 
926 {
927  uint8_t temp;
928 
929  //Read the Global Port Mirroring and Snooping Control register
930  temp = ksz9896ReadSwitchReg8(interface,
932 
933  //Enable or disable IGMP snooping
934  if(enable)
935  {
937  }
938  else
939  {
941  }
942 
943  //Write the value back to Global Port Mirroring and Snooping Control register
945  temp);
946 }
947 
948 
949 /**
950  * @brief Enable MLD snooping
951  * @param[in] interface Underlying network interface
952  * @param[in] enable Enable or disable MLD snooping
953  **/
954 
956 {
957  uint8_t temp;
958 
959  //Read the Global Port Mirroring and Snooping Control register
960  temp = ksz9896ReadSwitchReg8(interface,
962 
963  //Enable or disable MLD snooping
964  if(enable)
965  {
967  }
968  else
969  {
971  }
972 
973  //Write the value back to Global Port Mirroring and Snooping Control register
975  temp);
976 }
977 
978 
979 /**
980  * @brief Enable reserved multicast table
981  * @param[in] interface Underlying network interface
982  * @param[in] enable Enable or disable reserved group addresses
983  **/
984 
986 {
987  uint8_t temp;
988 
989  //Read the Switch Lookup Engine Control 0 register
991 
992  //Enable or disable the reserved multicast table
993  if(enable)
994  {
996  }
997  else
998  {
1000  }
1001 
1002  //Write the value back to Switch Lookup Engine Control 0 register
1004 }
1005 
1006 
1007 /**
1008  * @brief Add a new entry to the static MAC table
1009  * @param[in] interface Underlying network interface
1010  * @param[in] entry Pointer to the forwarding database entry
1011  * @return Error code
1012  **/
1013 
1015  const SwitchFdbEntry *entry)
1016 {
1017  error_t error;
1018  uint_t i;
1019  uint_t j;
1020  uint32_t value;
1021  SwitchFdbEntry currentEntry;
1022 
1023  //Keep track of the first free entry
1025 
1026  //Loop through the static MAC table
1027  for(i = 0; i < KSZ9896_STATIC_MAC_TABLE_SIZE; i++)
1028  {
1029  //Read current entry
1030  error = ksz9896GetStaticFdbEntry(interface, i, &currentEntry);
1031 
1032  //Valid entry?
1033  if(!error)
1034  {
1035  //Check whether the table already contains the specified MAC address
1036  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
1037  {
1038  j = i;
1039  break;
1040  }
1041  }
1042  else
1043  {
1044  //Keep track of the first free entry
1046  {
1047  j = i;
1048  }
1049  }
1050  }
1051 
1052  //Any entry available?
1054  {
1055  //Write the Static Address Table Entry 1 register
1058 
1059  //Set the relevant forward ports
1060  if(entry->destPorts == SWITCH_CPU_PORT_MASK)
1061  {
1063  }
1064  else
1065  {
1066  value = entry->destPorts & KSZ9896_PORT_MASK;
1067  }
1068 
1069  //Enable overriding of port state
1070  if(entry->override)
1071  {
1073  }
1074 
1075  //Write the Static Address Table Entry 2 register
1077 
1078  //Copy MAC address (first 16 bits)
1079  value = (entry->macAddr.b[0] << 8) | entry->macAddr.b[1];
1080 
1081  //Write the Static Address Table Entry 3 register
1083 
1084  //Copy MAC address (last 32 bits)
1085  value = (entry->macAddr.b[2] << 24) | (entry->macAddr.b[3] << 16) |
1086  (entry->macAddr.b[4] << 8) | entry->macAddr.b[5];
1087 
1088  //Write the Static Address Table Entry 4 register
1090 
1091  //Write the TABLE_INDEX field with the 4-bit index value
1093  //Set the TABLE_SELECT bit to 0 to select the static address table
1095  //Set the ACTION bit to 0 to indicate a write operation
1097  //Set the START_FINISH bit to 1 to initiate the operation
1099 
1100  //Start the write operation
1102  value);
1103 
1104  //When the operation is complete, the START_FINISH bit will be cleared
1105  //automatically
1106  do
1107  {
1108  //Read the Static Address and Reserved Multicast Table Control register
1109  value = ksz9896ReadSwitchReg32(interface,
1111 
1112  //Poll the START_FINISH bit
1114 
1115  //Successful processing
1116  error = NO_ERROR;
1117  }
1118  else
1119  {
1120  //The static MAC table is full
1121  error = ERROR_TABLE_FULL;
1122  }
1123 
1124  //Return status code
1125  return error;
1126 }
1127 
1128 
1129 /**
1130  * @brief Remove an entry from the static MAC table
1131  * @param[in] interface Underlying network interface
1132  * @param[in] entry Forwarding database entry to remove from the table
1133  * @return Error code
1134  **/
1135 
1137  const SwitchFdbEntry *entry)
1138 {
1139  error_t error;
1140  uint_t j;
1141  uint32_t value;
1142  SwitchFdbEntry currentEntry;
1143 
1144  //Loop through the static MAC table
1145  for(j = 0; j < KSZ9896_STATIC_MAC_TABLE_SIZE; j++)
1146  {
1147  //Read current entry
1148  error = ksz9896GetStaticFdbEntry(interface, j, &currentEntry);
1149 
1150  //Valid entry?
1151  if(!error)
1152  {
1153  //Check whether the table contains the specified MAC address
1154  if(macCompAddr(&currentEntry.macAddr, &entry->macAddr))
1155  {
1156  break;
1157  }
1158  }
1159  }
1160 
1161  //Any matching entry?
1163  {
1164  //Clear Static Address Table Entry registers
1169 
1170  //Write the TABLE_INDEX field with the 4-bit index value
1172  //Set the TABLE_SELECT bit to 0 to select the static address table
1174  //Set the ACTION bit to 0 to indicate a write operation
1176  //Set the START_FINISH bit to 1 to initiate the operation
1178 
1179  //Start the write operation
1181  value);
1182 
1183  //When the operation is complete, the START_FINISH bit will be cleared
1184  //automatically
1185  do
1186  {
1187  //Read the Static Address and Reserved Multicast Table Control register
1188  value = ksz9896ReadSwitchReg32(interface,
1190 
1191  //Poll the START_FINISH bit
1193 
1194  //Successful processing
1195  error = NO_ERROR;
1196  }
1197  else
1198  {
1199  //The static MAC table does not contain the specified address
1200  error = ERROR_NOT_FOUND;
1201  }
1202 
1203  //Return status code
1204  return error;
1205 }
1206 
1207 
1208 /**
1209  * @brief Read an entry from the static MAC table
1210  * @param[in] interface Underlying network interface
1211  * @param[in] index Zero-based index of the entry to read
1212  * @param[out] entry Pointer to the forwarding database entry
1213  * @return Error code
1214  **/
1215 
1217  SwitchFdbEntry *entry)
1218 {
1219  error_t error;
1220  uint32_t value;
1221 
1222  //Check index parameter
1223  if(index < KSZ9896_STATIC_MAC_TABLE_SIZE)
1224  {
1225  //Write the TABLE_INDEX field with the 4-bit index value
1227  //Set the TABLE_SELECT bit to 0 to select the static address table
1229  //Set the ACTION bit to 1 to indicate a read operation
1231  //Set the START_FINISH bit to 1 to initiate the operation
1233 
1234  //Start the read operation
1236  value);
1237 
1238  //When the operation is complete, the START_FINISH bit will be cleared
1239  //automatically
1240  do
1241  {
1242  //Read the Static Address and Reserved Multicast Table Control register
1243  value = ksz9896ReadSwitchReg32(interface,
1245 
1246  //Poll the START_FINISH bit
1248 
1249  //Read the Static Address Table Entry 1 register
1251 
1252  //Valid entry?
1254  {
1255  //Read the Static Address Table Entry 2 register
1257 
1258  //Retrieve the ports associated with this MAC address
1259  entry->srcPort = 0;
1261 
1262  //Check the value of the OVERRIDE bit
1264  {
1265  entry->override = TRUE;
1266  }
1267  else
1268  {
1269  entry->override = FALSE;
1270  }
1271 
1272  //Read the Static Address Table Entry 3 register
1274 
1275  //Copy MAC address (first 16 bits)
1276  entry->macAddr.b[0] = (value >> 8) & 0xFF;
1277  entry->macAddr.b[1] = value & 0xFF;
1278 
1279  //Read the Static Address Table Entry 4 register
1281 
1282  //Copy MAC address (last 32 bits)
1283  entry->macAddr.b[2] = (value >> 24) & 0xFF;
1284  entry->macAddr.b[3] = (value >> 16) & 0xFF;
1285  entry->macAddr.b[4] = (value >> 8) & 0xFF;
1286  entry->macAddr.b[5] = value & 0xFF;
1287 
1288  //Successful processing
1289  error = NO_ERROR;
1290  }
1291  else
1292  {
1293  //The entry is not valid
1294  error = ERROR_INVALID_ENTRY;
1295  }
1296  }
1297  else
1298  {
1299  //The end of the table has been reached
1300  error = ERROR_END_OF_TABLE;
1301  }
1302 
1303  //Return status code
1304  return error;
1305 }
1306 
1307 
1308 /**
1309  * @brief Flush static MAC table
1310  * @param[in] interface Underlying network interface
1311  **/
1312 
1314 {
1315  uint_t i;
1316  uint32_t value;
1317 
1318  //Loop through the static MAC table
1319  for(i = 0; i < KSZ9896_STATIC_MAC_TABLE_SIZE; i++)
1320  {
1321  //Clear Static Address Table Entry registers
1326 
1327  //Write the TABLE_INDEX field with the 4-bit index value
1329  //Set the TABLE_SELECT bit to 0 to select the static address table
1331  //Set the ACTION bit to 0 to indicate a write operation
1333  //Set the START_FINISH bit to 1 to initiate the operation
1335 
1336  //Start the write operation
1338  value);
1339 
1340  //When the operation is complete, the START_FINISH bit will be cleared
1341  //automatically
1342  do
1343  {
1344  //Read the Static Address and Reserved Multicast Table Control register
1345  value = ksz9896ReadSwitchReg32(interface,
1347 
1348  //Poll the START_FINISH bit
1350  }
1351 }
1352 
1353 
1354 /**
1355  * @brief Read an entry from the dynamic MAC table
1356  * @param[in] interface Underlying network interface
1357  * @param[in] index Zero-based index of the entry to read
1358  * @param[out] entry Pointer to the forwarding database entry
1359  * @return Error code
1360  **/
1361 
1363  SwitchFdbEntry *entry)
1364 {
1365  error_t error;
1366  uint32_t value;
1367 
1368  //First entry?
1369  if(index == 0)
1370  {
1371  //Clear the ALU Table Access Control register to stop any operation
1373 
1374  //Start the search operation
1378  }
1379 
1380  //Poll the VALID_ENTRY_OR_SEARCH_END bit until it is set
1381  do
1382  {
1383  //Read the ALU Table Access Control register
1385 
1386  //This bit goes high to indicate either a new valid entry is returned or
1387  //the search is complete
1389 
1390  //Check whether the next valid entry is ready
1391  if((value & KSZ9896_ALU_TABLE_CTRL_VALID) != 0)
1392  {
1393  //Store the data from the ALU table entry
1394  entry->destPorts = 0;
1395  entry->override = FALSE;
1396 
1397  //Read the ALU Table Entry 1 and 2 registers
1400 
1401  //Retrieve the port associated with this MAC address
1403  {
1405  entry->srcPort = KSZ9896_PORT1;
1406  break;
1408  entry->srcPort = KSZ9896_PORT2;
1409  break;
1411  entry->srcPort = KSZ9896_PORT3;
1412  break;
1414  entry->srcPort = KSZ9896_PORT4;
1415  break;
1417  entry->srcPort = KSZ9896_PORT5;
1418  break;
1420  entry->srcPort = KSZ9896_PORT6;
1421  break;
1422  default:
1423  entry->srcPort = 0;
1424  break;
1425  }
1426 
1427  //Read the ALU Table Entry 3 register
1429 
1430  //Copy MAC address (first 16 bits)
1431  entry->macAddr.b[0] = (value >> 8) & 0xFF;
1432  entry->macAddr.b[1] = value & 0xFF;
1433 
1434  //Read the ALU Table Entry 4 register
1436 
1437  //Copy MAC address (last 32 bits)
1438  entry->macAddr.b[2] = (value >> 24) & 0xFF;
1439  entry->macAddr.b[3] = (value >> 16) & 0xFF;
1440  entry->macAddr.b[4] = (value >> 8) & 0xFF;
1441  entry->macAddr.b[5] = value & 0xFF;
1442 
1443  //Successful processing
1444  error = NO_ERROR;
1445  }
1446  else
1447  {
1448  //The search can be stopped any time by setting the START_FINISH bit to 0
1450 
1451  //The end of the table has been reached
1452  error = ERROR_END_OF_TABLE;
1453  }
1454 
1455  //Return status code
1456  return error;
1457 }
1458 
1459 
1460 /**
1461  * @brief Flush dynamic MAC table
1462  * @param[in] interface Underlying network interface
1463  * @param[in] port Port number
1464  **/
1465 
1467 {
1468  uint_t temp;
1469  uint8_t state;
1470 
1471  //Flush only dynamic table entries
1476 
1477  //Valid port number?
1478  if(port >= KSZ9896_PORT1 && port <= KSZ9896_PORT6)
1479  {
1480  //Save the current state of the port
1482 
1483  //Turn off learning capability
1486 
1487  //All the entries associated with a port that has its learning capability
1488  //being turned off will be flushed
1492 
1493  //Restore the original state of the port
1495  }
1496  else
1497  {
1498  //Trigger a flush of the entire address lookup table
1502  }
1503 }
1504 
1505 
1506 /**
1507  * @brief Set forward ports for unknown multicast packets
1508  * @param[in] interface Underlying network interface
1509  * @param[in] enable Enable or disable forwarding of unknown multicast packets
1510  * @param[in] forwardPorts Port map
1511  **/
1512 
1514  bool_t enable, uint32_t forwardPorts)
1515 {
1516  uint32_t temp;
1517 
1518  //Read Unknown Multicast Control register
1520 
1521  //Clear port map
1523 
1524  //Enable or disable forwarding of unknown multicast packets
1525  if(enable)
1526  {
1527  //Enable forwarding
1529 
1530  //Check whether unknown multicast packets should be forwarded to the CPU port
1531  if((forwardPorts & SWITCH_CPU_PORT_MASK) != 0)
1532  {
1534  }
1535 
1536  //Select the desired forward ports
1537  temp |= forwardPorts & KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP_ALL;
1538  }
1539  else
1540  {
1541  //Disable forwarding
1543  }
1544 
1545  //Write the value back to Unknown Multicast Control register
1547 }
1548 
1549 
1550 /**
1551  * @brief Write PHY register
1552  * @param[in] interface Underlying network interface
1553  * @param[in] port Port number
1554  * @param[in] address PHY register address
1555  * @param[in] data Register value
1556  **/
1557 
1558 void ksz9896WritePhyReg(NetInterface *interface, uint8_t port,
1559  uint8_t address, uint16_t data)
1560 {
1561  uint16_t n;
1562 
1563  //SPI slave mode?
1564  if(interface->spiDriver != NULL)
1565  {
1566  //The SPI interface provides access to all PHY registers
1568  //Write the 16-bit value
1569  ksz9896WriteSwitchReg16(interface, n, data);
1570  }
1571  else if(interface->smiDriver != NULL)
1572  {
1573  //Write the specified PHY register
1574  interface->smiDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1575  }
1576  else
1577  {
1578  //Write the specified PHY register
1579  interface->nicDriver->writePhyReg(SMI_OPCODE_WRITE, port, address, data);
1580  }
1581 }
1582 
1583 
1584 /**
1585  * @brief Read PHY register
1586  * @param[in] interface Underlying network interface
1587  * @param[in] port Port number
1588  * @param[in] address PHY register address
1589  * @return Register value
1590  **/
1591 
1592 uint16_t ksz9896ReadPhyReg(NetInterface *interface, uint8_t port,
1593  uint8_t address)
1594 {
1595  uint16_t n;
1596  uint16_t data;
1597 
1598  //SPI slave mode?
1599  if(interface->spiDriver != NULL)
1600  {
1601  //The SPI interface provides access to all PHY registers
1603  //Read the 16-bit value
1604  data = ksz9896ReadSwitchReg16(interface, n);
1605  }
1606  else if(interface->smiDriver != NULL)
1607  {
1608  //Read the specified PHY register
1609  data = interface->smiDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1610  }
1611  else
1612  {
1613  //Read the specified PHY register
1614  data = interface->nicDriver->readPhyReg(SMI_OPCODE_READ, port, address);
1615  }
1616 
1617  //Return register value
1618  return data;
1619 }
1620 
1621 
1622 /**
1623  * @brief Dump PHY registers for debugging purpose
1624  * @param[in] interface Underlying network interface
1625  * @param[in] port Port number
1626  **/
1627 
1628 void ksz9896DumpPhyReg(NetInterface *interface, uint8_t port)
1629 {
1630  uint8_t i;
1631 
1632  //Loop through PHY registers
1633  for(i = 0; i < 32; i++)
1634  {
1635  //Display current PHY register
1636  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
1637  ksz9896ReadPhyReg(interface, port, i));
1638  }
1639 
1640  //Terminate with a line feed
1641  TRACE_DEBUG("\r\n");
1642 }
1643 
1644 
1645 /**
1646  * @brief Write MMD register
1647  * @param[in] interface Underlying network interface
1648  * @param[in] port Port number
1649  * @param[in] devAddr Device address
1650  * @param[in] regAddr Register address
1651  * @param[in] data Register value
1652  **/
1653 
1654 void ksz9896WriteMmdReg(NetInterface *interface, uint8_t port,
1655  uint8_t devAddr, uint16_t regAddr, uint16_t data)
1656 {
1657  //Select register operation
1660 
1661  //Write MMD register address
1663 
1664  //Select data operation
1667 
1668  //Write the content of the MMD register
1670 }
1671 
1672 
1673 /**
1674  * @brief Read MMD register
1675  * @param[in] interface Underlying network interface
1676  * @param[in] port Port number
1677  * @param[in] devAddr Device address
1678  * @param[in] regAddr Register address
1679  * @return Register value
1680  **/
1681 
1682 uint16_t ksz9896ReadMmdReg(NetInterface *interface, uint8_t port,
1683  uint8_t devAddr, uint16_t regAddr)
1684 {
1685  //Select register operation
1688 
1689  //Write MMD register address
1691 
1692  //Select data operation
1695 
1696  //Read the content of the MMD register
1697  return ksz9896ReadPhyReg(interface, port, KSZ9896_MMDAADR);
1698 }
1699 
1700 
1701 /**
1702  * @brief Write switch register (8 bits)
1703  * @param[in] interface Underlying network interface
1704  * @param[in] address Switch register address
1705  * @param[in] data Register value
1706  **/
1707 
1708 void ksz9896WriteSwitchReg8(NetInterface *interface, uint16_t address,
1709  uint8_t data)
1710 {
1711  uint32_t command;
1712 
1713  //SPI slave mode?
1714  if(interface->spiDriver != NULL)
1715  {
1716  //Set up a write operation
1717  command = KSZ9896_SPI_CMD_WRITE;
1718  //Set register address
1719  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1720 
1721  //Pull the CS pin low
1722  interface->spiDriver->assertCs();
1723 
1724  //Write 32-bit command
1725  interface->spiDriver->transfer((command >> 24) & 0xFF);
1726  interface->spiDriver->transfer((command >> 16) & 0xFF);
1727  interface->spiDriver->transfer((command >> 8) & 0xFF);
1728  interface->spiDriver->transfer(command & 0xFF);
1729 
1730  //Write 8-bit data
1731  interface->spiDriver->transfer(data);
1732 
1733  //Terminate the operation by raising the CS pin
1734  interface->spiDriver->deassertCs();
1735  }
1736  else
1737  {
1738  //The MDC/MDIO interface does not have access to all the configuration
1739  //registers. It can only access the standard MIIM registers
1740  }
1741 }
1742 
1743 
1744 /**
1745  * @brief Read switch register (8 bits)
1746  * @param[in] interface Underlying network interface
1747  * @param[in] address Switch register address
1748  * @return Register value
1749  **/
1750 
1751 uint8_t ksz9896ReadSwitchReg8(NetInterface *interface, uint16_t address)
1752 {
1753  uint8_t data;
1754  uint32_t command;
1755 
1756  //SPI slave mode?
1757  if(interface->spiDriver != NULL)
1758  {
1759  //Set up a read operation
1760  command = KSZ9896_SPI_CMD_READ;
1761  //Set register address
1762  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1763 
1764  //Pull the CS pin low
1765  interface->spiDriver->assertCs();
1766 
1767  //Write 32-bit command
1768  interface->spiDriver->transfer((command >> 24) & 0xFF);
1769  interface->spiDriver->transfer((command >> 16) & 0xFF);
1770  interface->spiDriver->transfer((command >> 8) & 0xFF);
1771  interface->spiDriver->transfer(command & 0xFF);
1772 
1773  //Read 8-bit data
1774  data = interface->spiDriver->transfer(0xFF);
1775 
1776  //Terminate the operation by raising the CS pin
1777  interface->spiDriver->deassertCs();
1778  }
1779  else
1780  {
1781  //The MDC/MDIO interface does not have access to all the configuration
1782  //registers. It can only access the standard MIIM registers
1783  data = 0;
1784  }
1785 
1786  //Return register value
1787  return data;
1788 }
1789 
1790 
1791 /**
1792  * @brief Write switch register (16 bits)
1793  * @param[in] interface Underlying network interface
1794  * @param[in] address Switch register address
1795  * @param[in] data Register value
1796  **/
1797 
1799  uint16_t data)
1800 {
1801  uint32_t command;
1802 
1803  //SPI slave mode?
1804  if(interface->spiDriver != NULL)
1805  {
1806  //Set up a write operation
1807  command = KSZ9896_SPI_CMD_WRITE;
1808  //Set register address
1809  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1810 
1811  //Pull the CS pin low
1812  interface->spiDriver->assertCs();
1813 
1814  //Write 32-bit command
1815  interface->spiDriver->transfer((command >> 24) & 0xFF);
1816  interface->spiDriver->transfer((command >> 16) & 0xFF);
1817  interface->spiDriver->transfer((command >> 8) & 0xFF);
1818  interface->spiDriver->transfer(command & 0xFF);
1819 
1820  //Write 16-bit data
1821  interface->spiDriver->transfer((data >> 8) & 0xFF);
1822  interface->spiDriver->transfer(data & 0xFF);
1823 
1824  //Terminate the operation by raising the CS pin
1825  interface->spiDriver->deassertCs();
1826  }
1827  else
1828  {
1829  //The MDC/MDIO interface does not have access to all the configuration
1830  //registers. It can only access the standard MIIM registers
1831  }
1832 }
1833 
1834 
1835 /**
1836  * @brief Read switch register (16 bits)
1837  * @param[in] interface Underlying network interface
1838  * @param[in] address Switch register address
1839  * @return Register value
1840  **/
1841 
1842 uint16_t ksz9896ReadSwitchReg16(NetInterface *interface, uint16_t address)
1843 {
1844  uint16_t data;
1845  uint32_t command;
1846 
1847  //SPI slave mode?
1848  if(interface->spiDriver != NULL)
1849  {
1850  //Set up a read operation
1851  command = KSZ9896_SPI_CMD_READ;
1852  //Set register address
1853  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1854 
1855  //Pull the CS pin low
1856  interface->spiDriver->assertCs();
1857 
1858  //Write 32-bit command
1859  interface->spiDriver->transfer((command >> 24) & 0xFF);
1860  interface->spiDriver->transfer((command >> 16) & 0xFF);
1861  interface->spiDriver->transfer((command >> 8) & 0xFF);
1862  interface->spiDriver->transfer(command & 0xFF);
1863 
1864  //Read 16-bit data
1865  data = interface->spiDriver->transfer(0xFF) << 8;
1866  data |= interface->spiDriver->transfer(0xFF);
1867 
1868  //Terminate the operation by raising the CS pin
1869  interface->spiDriver->deassertCs();
1870  }
1871  else
1872  {
1873  //The MDC/MDIO interface does not have access to all the configuration
1874  //registers. It can only access the standard MIIM registers
1875  data = 0;
1876  }
1877 
1878  //Return register value
1879  return data;
1880 }
1881 
1882 
1883 /**
1884  * @brief Write switch register (32 bits)
1885  * @param[in] interface Underlying network interface
1886  * @param[in] address Switch register address
1887  * @param[in] data Register value
1888  **/
1889 
1891  uint32_t data)
1892 {
1893  uint32_t command;
1894 
1895  //SPI slave mode?
1896  if(interface->spiDriver != NULL)
1897  {
1898  //Set up a write operation
1899  command = KSZ9896_SPI_CMD_WRITE;
1900  //Set register address
1901  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1902 
1903  //Pull the CS pin low
1904  interface->spiDriver->assertCs();
1905 
1906  //Write 32-bit command
1907  interface->spiDriver->transfer((command >> 24) & 0xFF);
1908  interface->spiDriver->transfer((command >> 16) & 0xFF);
1909  interface->spiDriver->transfer((command >> 8) & 0xFF);
1910  interface->spiDriver->transfer(command & 0xFF);
1911 
1912  //Write 32-bit data
1913  interface->spiDriver->transfer((data >> 24) & 0xFF);
1914  interface->spiDriver->transfer((data >> 16) & 0xFF);
1915  interface->spiDriver->transfer((data >> 8) & 0xFF);
1916  interface->spiDriver->transfer(data & 0xFF);
1917 
1918  //Terminate the operation by raising the CS pin
1919  interface->spiDriver->deassertCs();
1920  }
1921  else
1922  {
1923  //The MDC/MDIO interface does not have access to all the configuration
1924  //registers. It can only access the standard MIIM registers
1925  }
1926 }
1927 
1928 
1929 /**
1930  * @brief Read switch register (32 bits)
1931  * @param[in] interface Underlying network interface
1932  * @param[in] address Switch register address
1933  * @return Register value
1934  **/
1935 
1936 uint32_t ksz9896ReadSwitchReg32(NetInterface *interface, uint16_t address)
1937 {
1938  uint32_t data;
1939  uint32_t command;
1940 
1941  //SPI slave mode?
1942  if(interface->spiDriver != NULL)
1943  {
1944  //Set up a read operation
1945  command = KSZ9896_SPI_CMD_READ;
1946  //Set register address
1947  command |= (address << 5) & KSZ9896_SPI_CMD_ADDR;
1948 
1949  //Pull the CS pin low
1950  interface->spiDriver->assertCs();
1951 
1952  //Write 32-bit command
1953  interface->spiDriver->transfer((command >> 24) & 0xFF);
1954  interface->spiDriver->transfer((command >> 16) & 0xFF);
1955  interface->spiDriver->transfer((command >> 8) & 0xFF);
1956  interface->spiDriver->transfer(command & 0xFF);
1957 
1958  //Read 32-bit data
1959  data = interface->spiDriver->transfer(0xFF) << 24;
1960  data |= interface->spiDriver->transfer(0xFF) << 16;
1961  data |= interface->spiDriver->transfer(0xFF) << 8;
1962  data |= interface->spiDriver->transfer(0xFF);
1963 
1964  //Terminate the operation by raising the CS pin
1965  interface->spiDriver->deassertCs();
1966  }
1967  else
1968  {
1969  //The MDC/MDIO interface does not have access to all the configuration
1970  //registers. It can only access the standard MIIM registers
1971  data = 0;
1972  }
1973 
1974  //Return register value
1975  return data;
1976 }
void nicNotifyLinkChange(NetInterface *interface)
Process link state change notification.
Definition: nic.c:601
#define KSZ9896_PORT6_OP_CTRL0
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP_ALL
@ NIC_LINK_SPEED_1GBPS
Definition: nic.h:113
void ksz9896WritePhyReg(NetInterface *interface, uint8_t port, uint8_t address, uint16_t data)
Write PHY register.
void ksz9896DumpPhyReg(NetInterface *interface, uint8_t port)
Dump PHY registers for debugging purpose.
#define NetContext
Definition: net.h:36
void ksz9896WriteMmdReg(NetInterface *interface, uint8_t port, uint8_t devAddr, uint16_t regAddr, uint16_t data)
Write MMD register.
#define KSZ9896_SPI_CMD_ADDR
int bool_t
Definition: compiler_port.h:63
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_START_FINISH
@ ERROR_NOT_FOUND
Definition: error.h:148
#define KSZ9896_SWITCH_LUE_CTRL0_HASH_OPTION_CRC
@ NIC_LINK_SPEED_UNKNOWN
Definition: nic.h:110
uint32_t destPorts
Definition: nic.h:152
error_t ksz9896GetDynamicFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the dynamic MAC table.
@ NIC_FULL_DUPLEX_MODE
Definition: nic.h:125
#define KSZ9896_SWITCH_LUE_CTRL3
#define KSZ9896_STATIC_TABLE_ENTRY2
@ SWITCH_PORT_STATE_LISTENING
Definition: nic.h:138
#define KSZ9896_ALU_TABLE_CTRL
void ksz9896WriteSwitchReg16(NetInterface *interface, uint16_t address, uint16_t data)
Write switch register (16 bits)
#define KSZ9896_PHYCON_SPEED_100BTX
#define KSZ9896_MMDACR_DEVAD
#define KSZ9896_PORT_MASK
#define KSZ9896_SWITCH_OP_START_SWITCH
#define KSZ9896_SPI_CMD_WRITE
@ ERROR_END_OF_TABLE
Definition: error.h:292
#define KSZ9896_STATIC_MCAST_TABLE_CTRL
@ SWITCH_PORT_STATE_DISABLED
Definition: nic.h:136
#define KSZ9896_SPI_CMD_READ
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
error_t ksz9896GetStaticFdbEntry(NetInterface *interface, uint_t index, SwitchFdbEntry *entry)
Read an entry from the static MAC table.
#define TRUE
Definition: os_port.h:50
#define KSZ9896_PORT6_XMII_CTRL1
uint8_t data[]
Definition: ethernet.h:224
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD
void ksz9896WriteSwitchReg8(NetInterface *interface, uint16_t address, uint8_t data)
Write switch register (8 bits)
#define KSZ9896_MMD_LED_MODE_LED_MODE_TRI_COLOR_DUAL
void ksz9896EnableIgmpSnooping(NetInterface *interface, bool_t enable)
Enable IGMP snooping.
@ SWITCH_PORT_STATE_LEARNING
Definition: nic.h:139
#define KSZ9896_PORTn_MSTP_STATE_LEARNING_DIS
#define KSZ9896_PORT5
void ksz9896SetAgingTime(NetInterface *interface, uint32_t agingTime)
Set aging time for dynamic filtering entries.
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_TABLE_SELECT
#define KSZ9896_TAIL_TAG_DEST_PORT3
uint8_t type
Definition: coap_common.h:176
uint16_t ksz9896ReadMmdReg(NetInterface *interface, uint8_t port, uint8_t devAddr, uint16_t regAddr)
Read MMD register.
#define KSZ9896_ALU_TABLE_CTRL_ACTION_SEARCH
void ksz9896FlushStaticFdbTable(NetInterface *interface)
Flush static MAC table.
@ ERROR_INVALID_PORT
Definition: error.h:104
#define KSZ9896_SWITCH_LUE_CTRL1
#define KSZ9896_ALU_TABLE_ENTRY2_PORT2_FORWARD
#define KSZ9896_MMD_EEE_ADV
@ ERROR_TABLE_FULL
Definition: error.h:291
void ksz9896EnableMldSnooping(NetInterface *interface, bool_t enable)
Enable MLD snooping.
#define KSZ9896_SWITCH_MAC_CTRL0_FRAME_LEN_CHECK_EN
#define KSZ9896_SWITCH_OP
#define KSZ9896_ALU_TABLE_ENTRY2
#define KSZ9896_ALU_TABLE_ENTRY2_PORT1_FORWARD
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_ACTION
#define KSZ9896_SWITCH_LUE_CTRL2_FLUSH_OPTION
EthHeader
Definition: ethernet.h:225
error_t ksz9896TagFrame(NetInterface *interface, NetBuffer *buffer, size_t *offset, NetTxAncillary *ancillary)
Add tail tag to Ethernet frame.
#define KSZ9896_PORT6_XMII_CTRL1_RGMII_ID_EG
#define KSZ9896_MMDACR_FUNC_ADDR
#define KSZ9896_PHYCON
#define KSZ9896_SWITCH_LUE_CTRL0
__weak_func void ksz9896Tick(NetInterface *interface)
KSZ9896 timer handler.
#define SMI_OPCODE_WRITE
Definition: nic.h:66
#define KSZ9896_ALU_TABLE_ENTRY2_PORT4_FORWARD
#define KSZ9896_STATIC_TABLE_ENTRY3
#define KSZ9896_ALU_TABLE_ENTRY2_PORT6_FORWARD
#define KSZ9896_MMD_LED_MODE_RESERVED_DEFAULT
#define KSZ9896_ALU_TABLE_CTRL_VALID_ENTRY_OR_SEARCH_END
@ SWITCH_PORT_STATE_UNKNOWN
Definition: nic.h:135
#define KSZ9896_ALU_TABLE_ENTRY1
#define FALSE
Definition: os_port.h:46
#define KSZ9896_CHIP_ID1_DEFAULT
#define KSZ9896_SWITCH_LUE_CTRL3_AGE_PERIOD_DEFAULT
#define KSZ9896_ALU_TABLE_ENTRY3
#define KSZ9896_STATIC_TABLE_ENTRY4
error_t
Error codes.
Definition: error.h:43
#define KSZ9896_GLOBAL_PORT_MIRROR_SNOOP_CTRL_IGMP_SNOOP_EN
#define KSZ9896_ALU_TABLE_CTRL_VALID
#define KSZ9896_SWITCH_MAC_CTRL0
#define KSZ9896_PORT6_XMII_CTRL1_RGMII_ID_IG
#define KSZ9896_PORT6_XMII_CTRL0
#define KSZ9896_PORT6_XMII_CTRL0_SPEED_10_100
#define KSZ9896_TAIL_TAG_NORMAL_ADDR_LOOKUP
#define KSZ9896_SWITCH_LUE_CTRL1_FLUSH_ALU_TABLE
#define KSZ9896_STATIC_MCAST_TABLE_CTRL_TABLE_INDEX
void ksz9896FlushDynamicFdbTable(NetInterface *interface, uint8_t port)
Flush dynamic MAC table.
uint16_t ksz9896ReadSwitchReg16(NetInterface *interface, uint16_t address)
Read switch register (16 bits)
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:40
#define KSZ9896_PORT1
error_t ksz9896DeleteStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Remove an entry from the static MAC table.
@ NIC_LINK_SPEED_10MBPS
Definition: nic.h:111
@ ERROR_INVALID_LENGTH
Definition: error.h:111
#define KSZ9896_TAIL_TAG_DEST_PORT2
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP_PORT6
#define KSZ9896_TAIL_TAG_DEST_PORT1
#define KSZ9896_TAIL_TAG_DEST_PORT5
#define KSZ9896_PORT3
#define KSZ9896_PORTn_OP_CTRL0_TAIL_TAG_EN
#define KSZ9896_PORTn_MSTP_STATE_RECEIVE_EN
error_t ethPadFrame(NetBuffer *buffer, size_t *length)
Ethernet frame padding.
#define KSZ9896_SWITCH_LUE_CTRL2
void ksz9896EnableIrq(NetInterface *interface)
Enable interrupts.
#define KSZ9896_STATIC_TABLE_ENTRY1_VALID
#define NetTxAncillary
Definition: net_misc.h:36
#define KSZ9896_ALU_TABLE_ENTRY2_PORT5_FORWARD
#define SMI_OPCODE_READ
Definition: nic.h:67
SwitchPortState
Switch port state.
Definition: nic.h:134
#define KSZ9896_SWITCH_LUE_CTRL0_AGE_COUNT_DEFAULT
error_t ksz9896Init(NetInterface *interface)
KSZ9896 Ethernet switch initialization.
#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 MIN(a, b)
Definition: os_port.h:63
#define KSZ9896_PORTn_MSTP_STATE(port)
uint16_t ksz9896ReadPhyReg(NetInterface *interface, uint8_t port, uint8_t address)
Read PHY register.
NicDuplexMode ksz9896GetDuplexMode(NetInterface *interface, uint8_t port)
Get duplex mode.
const uint16_t ksz9896IngressTailTag[6]
Tail tag rules (host to KSZ9896)
#define KSZ9896_PORT6_MASK
#define KSZ9896_PHYCON_SPEED_10BT
#define KSZ9896_PHYCON_DUPLEX_STATUS
#define KSZ9896_PORT6_XMII_CTRL1_IF_TYPE
error_t ksz9896UntagFrame(NetInterface *interface, uint8_t **frame, size_t *length, NetRxAncillary *ancillary)
Decode tail tag from incoming Ethernet frame.
uint32_t ksz9896ReadSwitchReg32(NetInterface *interface, uint16_t address)
Read switch register (32 bits)
#define KSZ9896_ALU_TABLE_ENTRY2_PORT_FORWARD
#define KSZ9896_ALU_TABLE_ENTRY2_PORT3_FORWARD
@ SWITCH_PORT_STATE_FORWARDING
Definition: nic.h:140
uint16_t port
Definition: dns_common.h:270
#define KSZ9896_SWITCH_LUE_CTRL0_RESERVED_MCAST_LOOKUP_EN
#define KSZ9896_STATIC_MAC_TABLE_SIZE
#define KSZ9896_BMSR_LINK_STATUS
#define TRACE_DEBUG(...)
Definition: debug.h:119
#define KSZ9896_ALU_TABLE_ENTRY4
KSZ9896 6-port Gigabit Ethernet switch driver.
#define KSZ9896_GLOBAL_PORT_MIRROR_SNOOP_CTRL
uint16_t regAddr
__weak_func void ksz9896EventHandler(NetInterface *interface)
KSZ9896 event handler.
void ksz9896WriteSwitchReg32(NetInterface *interface, uint16_t address, uint32_t data)
Write switch register (32 bits)
Ethernet switch driver.
Definition: nic.h:325
#define KSZ9896_SWITCH_LUE_CTRL1_FLUSH_MSTP_ENTRIES
#define KSZ9896_PORTn_MSTP_STATE_TRANSMIT_EN
#define KSZ9896_MMDACR
#define HTONS(value)
Definition: cpu_endian.h:410
uint8_t n
const SwitchDriver ksz9896SwitchDriver
KSZ9896 Ethernet switch driver.
#define KSZ9896_MMDAADR
@ 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
Ipv6Addr address[]
Definition: ipv6.h:345
#define KSZ9896_TAIL_TAG_PORT_BLOCKING_OVERRIDE
#define KSZ9896_STATIC_TABLE_ENTRY1
NicDuplexMode
Duplex mode.
Definition: nic.h:122
#define KSZ9896_PORT2
uint8_t ksz9896ReadSwitchReg8(NetInterface *interface, uint16_t address)
Read switch register (8 bits)
MacAddr macAddr
Definition: nic.h:150
#define KSZ9896_CHIP_ID1
uint32_t ksz9896GetLinkSpeed(NetInterface *interface, uint8_t port)
Get link speed.
uint8_t srcPort
Definition: nic.h:151
@ NIC_HALF_DUPLEX_MODE
Definition: nic.h:124
#define KSZ9896_TAIL_TAG_SRC_PORT
#define KSZ9896_PORT4
uint8_t value[]
Definition: tcp.h:376
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
#define KSZ9896_STATIC_TABLE_ENTRY2_PORT_FORWARD
#define KSZ9896_BMSR
#define KSZ9896_TAIL_TAG_DEST_PORT4
void ksz9896SetUnknownMcastFwdPorts(NetInterface *interface, bool_t enable, uint32_t forwardPorts)
Set forward ports for unknown multicast packets.
__weak_func void ksz9896InitHook(NetInterface *interface)
KSZ9896 custom configuration.
#define KSZ9896_PORT6_XMII_CTRL0_DUPLEX
@ NIC_UNKNOWN_DUPLEX_MODE
Definition: nic.h:123
void ksz9896SetPortState(NetInterface *interface, uint8_t port, SwitchPortState state)
Set port state.
SwitchPortState ksz9896GetPortState(NetInterface *interface, uint8_t port)
Get port state.
#define KSZ9896_PHYCON_SPEED_1000BT
#define KSZ9896_SWITCH_LUE_CTRL2_FLUSH_OPTION_DYNAMIC
#define KSZ9896_PORT6
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
#define KSZ9896_GLOBAL_PORT_MIRROR_SNOOP_CTRL_MLD_SNOOP_EN
#define KSZ9896_MMDACR_FUNC_DATA_NO_POST_INC
void ksz9896DisableIrq(NetInterface *interface)
Disable interrupts.
#define SWITCH_CPU_PORT_MASK
Definition: nic.h:60
@ NIC_LINK_SPEED_100MBPS
Definition: nic.h:112
#define KSZ9896_UNKONWN_MULTICAST_CTRL_FWD_MAP
error_t ksz9896AddStaticFdbEntry(NetInterface *interface, const SwitchFdbEntry *entry)
Add a new entry to the static MAC table.
unsigned int uint_t
Definition: compiler_port.h:57
TCP/IP stack core.
#define KSZ9896_ALU_TABLE_CTRL_START_FINISH
#define KSZ9896_MMD_LED_MODE
#define KSZ9896_PORT6_XMII_CTRL1_SPEED_1000
#define KSZ9896_STATIC_TABLE_ENTRY2_OVERRIDE
#define KSZ9896_UNKONWN_MULTICAST_CTRL
Helper functions for Ethernet.
#define KSZ9896_PORT6_XMII_CTRL1_IF_TYPE_RGMII
void ksz9896EnableRsvdMcastTable(NetInterface *interface, bool_t enable)
Enable reserved multicast table.
@ NO_ERROR
Success.
Definition: error.h:44
bool_t override
Definition: nic.h:153
Debugging facilities.
Forwarding database entry.
Definition: nic.h:149
#define KSZ9896_PORTn_ETH_PHY_REG(port, addr)
bool_t ksz9896GetLinkState(NetInterface *interface, uint8_t port)
Get link state.