ksz8463_driver.c
Go to the documentation of this file.
1 /**
2  * @file ksz8463_driver.c
3  * @brief KSZ8463 Ethernet switch
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 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 1.9.2
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 KSZ8463 Ethernet switch driver
43  **/
44 
46 {
54 };
55 
56 
57 /**
58  * @brief Tail tag rules (host to KSZ8463)
59  **/
60 
61 const uint8_t ksz8463IngressTailTag[3] =
62 {
63  0,
66 };
67 
68 
69 /**
70  * @brief KSZ8463 Ethernet switch initialization
71  * @param[in] interface Underlying network interface
72  * @return Error code
73  **/
74 
76 {
77  uint_t port;
78  uint16_t temp;
79 
80  //Debug message
81  TRACE_INFO("Initializing KSZ8463...\r\n");
82 
83  //SPI slave mode?
84  if(interface->spiDriver != NULL)
85  {
86  //Initialize SPI
87  interface->spiDriver->init();
88 
89 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
90  //Tail tagging mode?
91  if(interface->port != 0)
92  {
93  //Enable tail tag feature
97 
98  //Loop through ports
100  {
101  //Disable packet transmission and switch address learning
103  temp &= ~PORT_CTRL2_TRANSMIT_EN;
106  }
107  }
108  else
109 #endif
110  {
111  //Disable tail tag feature
113  temp &= ~GLOBAL_CTRL8_TAIL_TAG_EN;
115 
116  //Loop through ports
118  {
119  //Enable transmission, reception and switch address learning
122  temp &= ~PORT_CTRL2_LEARNING_DIS;
124  }
125  }
126 
127  //Dump switch registers for debugging purpose
128  ksz8463DumpSwitchReg(interface);
129  }
130  else
131  {
132  //Loop through ports
134  {
135  //Debug message
136  TRACE_DEBUG("Port %u:\r\n", port);
137  //Dump PHY registers for debugging purpose
138  ksz8463DumpPhyReg(interface, port);
139  }
140  }
141 
142  //Force the TCP/IP stack to poll the link state at startup
143  interface->phyEvent = TRUE;
144  //Notify the TCP/IP stack of the event
146 
147  //Successful initialization
148  return NO_ERROR;
149 }
150 
151 
152 /**
153  * @brief Get link state
154  * @param[in] interface Underlying network interface
155  * @param[in] port Port number
156  * @return Link state
157  **/
158 
160 {
161  uint16_t status;
162  bool_t linkState;
163 
164  //Check port number
165  if(port >= KSZ8463_PORT1 && port <= KSZ8463_PORT2)
166  {
167  //Get exclusive access
169 
170  //SPI slave mode?
171  if(interface->spiDriver != NULL)
172  {
173  //Read port status register
174  status = ksz8463ReadSwitchReg(interface,
176 
177  //Retrieve current link state
178  linkState = (status & PORT_STAT_LINK_STATUS) ? TRUE : FALSE;
179  }
180  else
181  {
182  //Read status register
183  status = ksz8463ReadPhyReg(interface, port, KSZ8463_PHY_REG_BMSR);
184 
185  //Retrieve current link state
186  linkState = (status & BMSR_LINK_STATUS) ? TRUE : FALSE;
187  }
188 
189  //Release exclusive access
191  }
192  else
193  {
194  //The specified port number is not valid
195  linkState = FALSE;
196  }
197 
198  //Return link status
199  return linkState;
200 }
201 
202 
203 /**
204  * @brief KSZ8463 timer handler
205  * @param[in] interface Underlying network interface
206  **/
207 
208 void ksz8463Tick(NetInterface *interface)
209 {
210  uint_t port;
211  uint16_t status;
212  bool_t linkState;
213 
214 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
215  //Tail tagging mode?
216  if(interface->port != 0)
217  {
218  uint_t i;
219  NetInterface *virtualInterface;
220 
221  //SPI slave mode?
222  if(interface->spiDriver != NULL)
223  {
224  //Loop through network interfaces
225  for(i = 0; i < NET_INTERFACE_COUNT; i++)
226  {
227  //Point to the current interface
228  virtualInterface = &netInterface[i];
229 
230  //Check whether the current virtual interface is attached to the
231  //physical interface
232  if(virtualInterface == interface || virtualInterface->parent == interface)
233  {
234  //The tail tag is used to indicate the source/destination port
235  port = virtualInterface->port;
236 
237  //Valid port?
238  if(port >= KSZ8463_PORT1 && port <= KSZ8463_PORT2)
239  {
240  //Read port status register
241  status = ksz8463ReadSwitchReg(interface,
243 
244  //Retrieve current link state
245  linkState = (status & PORT_STAT_LINK_STATUS) ? TRUE : FALSE;
246 
247  //Link up or link down event?
248  if(linkState != virtualInterface->linkState)
249  {
250  //Set event flag
251  interface->phyEvent = TRUE;
252  //Notify the TCP/IP stack of the event
254  }
255  }
256  }
257  }
258  }
259  }
260  else
261 #endif
262  {
263  //Initialize link state
264  linkState = FALSE;
265 
266  //Loop through ports
268  {
269  //SPI slave mode?
270  if(interface->spiDriver != NULL)
271  {
272  //Read port status register
273  status = ksz8463ReadSwitchReg(interface,
275 
276  //Retrieve current link state
277  if(status & PORT_STAT_LINK_STATUS)
278  linkState = TRUE;
279  }
280  else
281  {
282  //Read status register
283  status = ksz8463ReadPhyReg(interface, port, KSZ8463_PHY_REG_BMSR);
284 
285  //Retrieve current link state
286  if(status & BMSR_LINK_STATUS)
287  linkState = TRUE;
288  }
289  }
290 
291  //Link up or link down event?
292  if(linkState != interface->linkState)
293  {
294  //Set event flag
295  interface->phyEvent = TRUE;
296  //Notify the TCP/IP stack of the event
298  }
299  }
300 }
301 
302 
303 /**
304  * @brief Enable interrupts
305  * @param[in] interface Underlying network interface
306  **/
307 
309 {
310 }
311 
312 
313 /**
314  * @brief Disable interrupts
315  * @param[in] interface Underlying network interface
316  **/
317 
319 {
320 }
321 
322 
323 /**
324  * @brief KSZ8463 event handler
325  * @param[in] interface Underlying network interface
326  **/
327 
329 {
330  uint_t port;
331  uint16_t status;
332  bool_t linkState;
333 
334 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
335  //Tail tagging mode?
336  if(interface->port != 0)
337  {
338  uint_t i;
339  NetInterface *virtualInterface;
340 
341  //SPI slave mode?
342  if(interface->spiDriver != NULL)
343  {
344  //Loop through network interfaces
345  for(i = 0; i < NET_INTERFACE_COUNT; i++)
346  {
347  //Point to the current interface
348  virtualInterface = &netInterface[i];
349 
350  //Check whether the current virtual interface is attached to the
351  //physical interface
352  if(virtualInterface == interface ||
353  virtualInterface->parent == interface)
354  {
355  //The tail tag is used to indicate the source/destination port
356  port = virtualInterface->port;
357 
358  //Valid port?
359  if(port >= KSZ8463_PORT1 && port <= KSZ8463_PORT2)
360  {
361  //Read port status register
362  status = ksz8463ReadSwitchReg(interface,
364 
365  //Retrieve current link state
366  linkState = (status & PORT_STAT_LINK_STATUS) ? TRUE : FALSE;
367 
368  //Link up event?
369  if(linkState && !virtualInterface->linkState)
370  {
371  //Adjust MAC configuration parameters for proper operation
372  interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
373  interface->duplexMode = NIC_FULL_DUPLEX_MODE;
374  interface->nicDriver->updateMacConfig(interface);
375 
376  //Check current speed
377  if(status & PORT_STAT_OP_SPEED)
378  virtualInterface->linkSpeed = NIC_LINK_SPEED_100MBPS;
379  else
380  virtualInterface->linkSpeed = NIC_LINK_SPEED_10MBPS;
381 
382  //Check duplex mode
383  if(status & PORT_STAT_OP_MODE)
384  virtualInterface->duplexMode = NIC_FULL_DUPLEX_MODE;
385  else
386  virtualInterface->duplexMode = NIC_HALF_DUPLEX_MODE;
387 
388  //Update link state
389  virtualInterface->linkState = TRUE;
390 
391  //Process link state change event
392  nicNotifyLinkChange(virtualInterface);
393  }
394  //Link down event
395  else if(!linkState && virtualInterface->linkState)
396  {
397  //Update link state
398  virtualInterface->linkState = FALSE;
399 
400  //Process link state change event
401  nicNotifyLinkChange(virtualInterface);
402  }
403  }
404  }
405  }
406  }
407  }
408  else
409 #endif
410  {
411  //Initialize link state
412  linkState = FALSE;
413 
414  //Loop through ports
416  {
417  //SPI slave mode?
418  if(interface->spiDriver != NULL)
419  {
420  //Read port status register
421  status = ksz8463ReadSwitchReg(interface,
423 
424  //Retrieve current link state
425  if(status & PORT_STAT_LINK_STATUS)
426  linkState = TRUE;
427  }
428  else
429  {
430  //Read status register
431  status = ksz8463ReadPhyReg(interface, port, KSZ8463_PHY_REG_BMSR);
432 
433  //Retrieve current link state
434  if(status & BMSR_LINK_STATUS)
435  linkState = TRUE;
436  }
437  }
438 
439  //Link up event?
440  if(linkState)
441  {
442  //Adjust MAC configuration parameters for proper operation
443  interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
444  interface->duplexMode = NIC_FULL_DUPLEX_MODE;
445  interface->nicDriver->updateMacConfig(interface);
446 
447  //Update link state
448  interface->linkState = TRUE;
449  }
450  else
451  {
452  //Update link state
453  interface->linkState = FALSE;
454  }
455 
456  //Process link state change event
457  nicNotifyLinkChange(interface);
458  }
459 }
460 
461 
462 /**
463  * @brief Add tail tag to Ethernet frame
464  * @param[in] interface Underlying network interface
465  * @param[in] buffer Multi-part buffer containing the payload
466  * @param[in,out] offset Offset to the first payload byte
467  * @param[in] port Switch port identifier
468  * @param[in,out] type Ethernet type
469  * @return Error code
470  **/
471 
473  size_t *offset, uint8_t port, uint16_t *type)
474 {
475 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
476  error_t error;
477  size_t length;
478  const uint8_t *tailTag;
479 
480  //Valid port?
481  if(port >= KSZ8463_PORT1 && port <= KSZ8463_PORT2)
482  {
483  //The one byte tail tagging is used to indicate the destination port
484  tailTag = &ksz8463IngressTailTag[port];
485 
486  //Retrieve the length of the frame
487  length = netBufferGetLength(buffer) - *offset;
488 
489  //The host controller should manually add padding to the packet before
490  //inserting the tail tag
491  error = ethPadFrame(buffer, &length);
492 
493  //Check status code
494  if(!error)
495  {
496  //The tail tag is inserted at the end of the packet, just before the CRC
497  error = netBufferAppend(buffer, tailTag, sizeof(uint8_t));
498  }
499  }
500  else
501  {
502  //Invalid port identifier
503  error = ERROR_WRONG_IDENTIFIER;
504  }
505 
506  //Return status code
507  return error;
508 #else
509  //Tail tagging mode is not implemented
510  return NO_ERROR;
511 #endif
512 }
513 
514 
515 /**
516  * @brief Decode tail tag from incoming Ethernet frame
517  * @param[in] interface Underlying network interface
518  * @param[in,out] frame Pointer to the received Ethernet frame
519  * @param[in,out] length Length of the frame, in bytes
520  * @param[out] port Switch port identifier
521  * @return Error code
522  **/
523 
524 error_t ksz8463UntagFrame(NetInterface *interface, uint8_t **frame,
525  size_t *length, uint8_t *port)
526 {
527 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
528  error_t error;
529  uint8_t *tailTag;
530 
531  //Valid Ethernet frame received?
532  if(*length >= (sizeof(EthHeader) + sizeof(uint8_t)))
533  {
534  //The tail tag is inserted at the end of the packet, just before the CRC
535  tailTag = *frame + *length - sizeof(uint8_t);
536 
537  //The one byte tail tagging is used to indicate the source port
538  *port = KSZ8463_TAIL_TAG_DECODE(*tailTag);
539 
540  //Strip tail tag from Ethernet frame
541  *length -= sizeof(uint8_t);
542 
543  //Successful processing
544  error = NO_ERROR;
545  }
546  else
547  {
548  //Drop the received frame
549  error = ERROR_INVALID_LENGTH;
550  }
551 
552  //Return status code
553  return error;
554 #else
555  //Tail tagging mode is not implemented
556  return NO_ERROR;
557 #endif
558 }
559 
560 
561 /**
562  * @brief Write PHY register
563  * @param[in] interface Underlying network interface
564  * @param[in] port Port number
565  * @param[in] address PHY register address
566  * @param[in] data Register value
567  **/
568 
569 void ksz8463WritePhyReg(NetInterface *interface, uint8_t port,
570  uint8_t address, uint16_t data)
571 {
572  //Write the specified PHY register
573  interface->nicDriver->writePhyReg(port, address, data);
574 }
575 
576 
577 /**
578  * @brief Read PHY register
579  * @param[in] interface Underlying network interface
580  * @param[in] port Port number
581  * @param[in] address PHY register address
582  * @return Register value
583  **/
584 
585 uint16_t ksz8463ReadPhyReg(NetInterface *interface, uint8_t port,
586  uint8_t address)
587 {
588  //Read the specified PHY register
589  return interface->nicDriver->readPhyReg(port, address);
590 }
591 
592 
593 /**
594  * @brief Dump PHY registers for debugging purpose
595  * @param[in] interface Underlying network interface
596  * @param[in] port Port number
597  **/
598 
599 void ksz8463DumpPhyReg(NetInterface *interface, uint8_t port)
600 {
601  uint8_t i;
602 
603  //Loop through PHY registers
604  for(i = 0; i < 32; i++)
605  {
606  //Display current PHY register
607  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
608  ksz8463ReadPhyReg(interface, port, i));
609  }
610 
611  //Terminate with a line feed
612  TRACE_DEBUG("\r\n");
613 }
614 
615 
616 /**
617  * @brief Write switch register
618  * @param[in] interface Underlying network interface
619  * @param[in] address PHY register address
620  * @param[in] data Register value
621  **/
622 
623 void ksz8463WriteSwitchReg(NetInterface *interface, uint16_t address,
624  uint16_t data)
625 {
626  uint16_t command;
627 
628  //SPI slave mode?
629  if(interface->spiDriver != NULL)
630  {
631  //Set up a write operation
632  command = KSZ8463_SPI_CMD_WRITE;
633 
634  //The byte enable bits are set to indicate which bytes will be
635  //transferred in the data phase
636  if(address & 0x02)
638  else
640 
641  //Set register address
642  command |= (address << 4) & KSZ8463_SPI_CMD_ADDR;
643 
644  //Pull the CS pin low
645  interface->spiDriver->assertCs();
646 
647  //Write 16-bit command
648  interface->spiDriver->transfer(MSB(command));
649  interface->spiDriver->transfer(LSB(command));
650 
651  //Write 16-bit data
652  interface->spiDriver->transfer(LSB(data));
653  interface->spiDriver->transfer(MSB(data));
654 
655  //Terminate the operation by raising the CS pin
656  interface->spiDriver->deassertCs();
657  }
658  else
659  {
660  //The MDC/MDIO interface does not have access to all the configuration
661  //registers. It can only access the standard MIIM registers
662  }
663 }
664 
665 
666 /**
667  * @brief Read switch register
668  * @param[in] interface Underlying network interface
669  * @param[in] address PHY register address
670  * @return Register value
671  **/
672 
673 uint16_t ksz8463ReadSwitchReg(NetInterface *interface, uint16_t address)
674 {
675  uint16_t data;
676  uint16_t command;
677 
678  //SPI slave mode?
679  if(interface->spiDriver != NULL)
680  {
681  //Set up a read operation
682  command = KSZ8463_SPI_CMD_READ;
683 
684  //The byte enable bits are set to indicate which bytes will be
685  //transferred in the data phase
686  if(address & 0x02)
688  else
690 
691  //Set register address
692  command |= (address << 4) & KSZ8463_SPI_CMD_ADDR;
693 
694  //Pull the CS pin low
695  interface->spiDriver->assertCs();
696 
697  //Write 16-bit command
698  interface->spiDriver->transfer(MSB(command));
699  interface->spiDriver->transfer(LSB(command));
700 
701  //Read 16-bit data
702  data = interface->spiDriver->transfer(0xFF);
703  data |= interface->spiDriver->transfer(0xFF) << 8;
704 
705  //Terminate the operation by raising the CS pin
706  interface->spiDriver->deassertCs();
707  }
708  else
709  {
710  //The MDC/MDIO interface does not have access to all the configuration
711  //registers. It can only access the standard MIIM registers
712  data = 0;
713  }
714 
715  //Return register value
716  return data;
717 }
718 
719 
720 /**
721  * @brief Dump switch registers for debugging purpose
722  * @param[in] interface Underlying network interface
723  **/
724 
726 {
727  uint16_t i;
728 
729  //Loop through switch registers
730  for(i = 0; i < 256; i += 2)
731  {
732  //Display current switch register
733  TRACE_DEBUG("0x%04" PRIX16 " (%03" PRIu16 ") : 0x%04" PRIX16 "\r\n",
734  i, i, ksz8463ReadSwitchReg(interface, i));
735  }
736 
737  //Terminate with a line feed
738  TRACE_DEBUG("\r\n");
739 }
#define KSZ8463_SPI_CMD_READ
__start_packed struct @114 EthHeader
Ethernet frame header.
void nicNotifyLinkChange(NetInterface *interface)
Process link state change notification.
Definition: nic.c:536
#define MSB(x)
Definition: os_port.h:58
void ksz8463WritePhyReg(NetInterface *interface, uint8_t port, uint8_t address, uint16_t data)
Write PHY register.
TCP/IP stack core.
Debugging facilities.
#define KSZ8463_TAIL_TAG_ENCODE(port)
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
#define KSZ8463_SW_REG_GLOBAL_CTRL8
#define KSZ8463_SW_REG_PORT_CTRL2(n)
#define PORT_CTRL2_LEARNING_DIS
#define BMSR_LINK_STATUS
Definition: ar8031_driver.h:97
Helper functions for Ethernet.
bool_t ksz8463GetLinkState(NetInterface *interface, uint8_t port)
Get link state.
error_t ksz8463TagFrame(NetInterface *interface, NetBuffer *buffer, size_t *offset, uint8_t port, uint16_t *type)
Add tail tag to Ethernet frame.
char_t type
void ksz8463DisableIrq(NetInterface *interface)
Disable interrupts.
#define LSB(x)
Definition: os_port.h:54
#define KSZ8463_SPI_CMD_B2
error_t ethPadFrame(NetBuffer *buffer, size_t *length)
Ethernet frame padding.
#define TRUE
Definition: os_port.h:50
#define KSZ8463_SPI_CMD_B3
#define KSZ8463_SPI_CMD_B1
PHY driver.
Definition: nic.h:199
#define KSZ8463_SPI_CMD_WRITE
#define KSZ8463_SPI_CMD_B0
#define PORT_STAT_OP_MODE
#define NET_INTERFACE_COUNT
Definition: net.h:109
#define KSZ8463_PORT1
uint16_t ksz8463ReadPhyReg(NetInterface *interface, uint8_t port, uint8_t address)
Read PHY register.
error_t ksz8463Init(NetInterface *interface)
KSZ8463 Ethernet switch initialization.
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
NetInterface netInterface[NET_INTERFACE_COUNT]
Definition: net.c:75
#define KSZ8463_PHY_REG_BMSR
#define TRACE_INFO(...)
Definition: debug.h:94
#define KSZ8463_SPI_CMD_ADDR
Success.
Definition: error.h:44
Ipv6Addr address
#define PORT_CTRL2_TRANSMIT_EN
OsEvent netEvent
Definition: net.c:73
uint16_t ksz8463ReadSwitchReg(NetInterface *interface, uint16_t address)
Read switch register.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
error_t
Error codes.
Definition: error.h:42
KSZ8463 Ethernet switch.
#define KSZ8463_SW_REG_PORT_STAT(n)
unsigned int uint_t
Definition: compiler_port.h:45
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:586
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
void ksz8463DumpSwitchReg(NetInterface *interface)
Dump switch registers for debugging purpose.
uint8_t data[]
Definition: dtls_misc.h:169
#define NetInterface
Definition: net.h:36
void ksz8463EnableIrq(NetInterface *interface)
Enable interrupts.
#define PORT_STAT_LINK_STATUS
#define GLOBAL_CTRL8_TAIL_TAG_EN
uint16_t port
Definition: dns_common.h:223
void ksz8463Tick(NetInterface *interface)
KSZ8463 timer handler.
void ksz8463DumpPhyReg(NetInterface *interface, uint8_t port)
Dump PHY registers for debugging purpose.
#define KSZ8463_PORT2
OsMutex netMutex
Definition: net.c:71
const uint8_t ksz8463IngressTailTag[3]
Tail tag rules (host to KSZ8463)
void ksz8463EventHandler(NetInterface *interface)
KSZ8463 event handler.
#define PORT_CTRL2_RECEIVE_EN
error_t ksz8463UntagFrame(NetInterface *interface, uint8_t **frame, size_t *length, uint8_t *port)
Decode tail tag from incoming Ethernet frame.
uint8_t length
Definition: dtls_misc.h:142
#define PORT_STAT_OP_SPEED
#define FALSE
Definition: os_port.h:46
void ksz8463WriteSwitchReg(NetInterface *interface, uint16_t address, uint16_t data)
Write switch register.
int bool_t
Definition: compiler_port.h:49
const PhyDriver ksz8463PhyDriver
KSZ8463 Ethernet switch driver.
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
#define KSZ8463_TAIL_TAG_DECODE(tag)
#define TRACE_DEBUG(...)
Definition: debug.h:106