source: rtems/c/src/lib/libbsp/arm/altera-cyclone-v/network/network.c @ c1c45f5

4.115
Last change on this file since c1c45f5 was e6a7896f, checked in by Sebastian Huber <sebastian.huber@…>, on 01/23/15 at 13:49:36

bsp/altera-cyclone-v: Use proper free function

  • Property mode set to 100644
File size: 31.6 KB
Line 
1/**
2 * @file
3 *
4 * @brief Network Driver
5 *
6 * This driver is a wrapper for the DWMAC 1000 driver from libchip.
7 * The DWMAC 1000 driver is an on-chip Synopsys IP Ethernet controllers
8 */
9
10/*
11 * Copyright (c) 2013 embedded brains GmbH.  All rights reserved.
12 *
13 *  embedded brains GmbH
14 *  Dornierstr. 4
15 *  82178 Puchheim
16 *  Germany
17 *  <rtems@embedded-brains.de>
18 *
19 * The license and distribution terms for this file may be
20 * found in the file LICENSE in this distribution or at
21 * http://www.rtems.org/license/LICENSE.
22 */
23
24
25#include <rtems.h>
26
27#if defined(RTEMS_NETWORKING)
28
29#include <assert.h>
30#include <stdint.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <rtems.h>
34#include <rtems/config.h>
35#include <bsp.h>
36#include <bsp/irq.h>
37#include <bsp/linker-symbols.h>
38#include <bsp/hwlib.h>
39#include <bsp/alt_clock_manager.h>
40#include <bsp/alt_generalpurpose_io.h>
41#include "socal/alt_rstmgr.h"
42#include "socal/alt_sysmgr.h"
43#include "socal/hps.h"
44#include "socal/socal.h"
45#include <libchip/dwmac.h>
46
47/** @brief Pin mask for the interrupt from the ethernet PHY */
48#define NETWORK_PIN_MASK_PHY_INTERRUPT 0x00000040U
49
50/** @brief Ethernet PHY status */
51typedef enum {
52  /** @brief The ethernet PHY is off */
53  NETWORK_IF_PHY_STATUS_OFF,
54
55  /** @brief The ethernet PHY is on an initialized, but stopped */
56  NETWORK_IF_PHY_STATUS_STOPPED,
57
58  /** @brief The ethernet PHY is started and will generate requested events */
59  NETWORK_IF_PHY_STATUS_STARTED,
60
61  /** @brief Number of ethernet PHY statuses */
62  NETWORK_IF_PHY_STATUS_COUNT
63} network_if_phy_status;
64
65/** @brief Phy context
66 *
67 * ethernet PHY device context
68 */
69typedef struct {
70  /** @brief PHY device status */
71  network_if_phy_status status;
72
73  /** @brief Interrupt mask which corresponds to currently requested events */
74  uint16_t irq_mask;
75} network_if_phy_context;
76
77/** @brief Phy context initializer
78 *
79 * @retval Initialized network PHY context
80 */
81#define NETWORK_IF_PHY_CONTEXT_INITIALIZER( \
82    ) \
83  { \
84    NETWORK_IF_PHY_STATUS_OFF, \
85    0 \
86  }
87
88/** @brief Network interface controller ID
89 *
90 * Identifies the network interface controller handled by the driver
91 */
92typedef enum {
93  /** @brief ID for EMAC0 */
94  NETWORK_IF_NIC_ID_EMAC0,
95
96  /** @brief ID for EMAC1 */
97  NETWORK_IF_NIC_ID_EMAC1,
98
99  /** @brief Number of network interface controller IDs */
100  NETWORK_IF_NIC_ID_COUNT
101} network_if_nic_id;
102
103/** @brief Network interface context
104 *
105 * Context for the micro controller specific part of the DWMAC 1000 n
106 * etwork driver
107 */
108typedef struct {
109  /** @brief Network interface controller ID */
110  network_if_nic_id nic_id;
111
112  /** @brief Driver context
113   *
114   * Address of the context of the low level DWMAC 1000 driver from libchip */
115  void *driver_context;
116
117  /** @brief Ethernet PHY context */
118  network_if_phy_context phy;
119
120  /* TODO: Create network context on micro controller level */
121} network_if_context;
122
123/** @brief Network interface context initializer
124 *
125 * Initializer for the context of a network interface
126 * @param nic_id          ID of the network interface controller
127 * @param driver_context Address of the context of the low level DWMAC 1000
128 *                       driver from libchip
129 * @retval Initialized network interface context
130 */
131#define NETWORK_IF_CONTEXT_INITIALIZER( \
132    nic_id, \
133    driver_context \
134    ) \
135  { \
136    nic_id, \
137    driver_context, \
138    NETWORK_IF_PHY_CONTEXT_INITIALIZER() \
139  }
140
141/******************************************************************************
142* Helper Methods
143******************************************************************************/
144
145/** @brief Altera status code to errno
146 *
147 * Convert Altera status code to errno error number
148 * @param status  Altera status code as returned by methods from Alteras HWLib
149 * @retval Converted errno error number from errno.h
150 */
151static int network_if_altera_status_code_to_errno( ALT_STATUS_CODE status )
152{
153  int eno = 0;
154
155  switch ( status ) {
156    /*!
157     * Indicates a FALSE condition.
158     */
159    case ALT_E_FALSE:
160
161    /*!
162     * Indicates a TRUE condition.
163     */
164    case ALT_E_TRUE:
165
166      /*! The operation was successful. */
167      /* case ALT_E_SUCCESS: */
168      eno = 0;
169      break;
170
171    /*! The operation failed. */
172    case ALT_E_ERROR:
173
174    /*! An invalid option was selected. */
175    case ALT_E_INV_OPTION:
176      eno = EIO; /* I/O error */
177      break;
178
179    /*! FPGA configuration error detected.*/
180    case ALT_E_FPGA_CFG:
181      eno = ECANCELED; /* Operation canceled */
182      break;
183
184    /*! FPGA CRC error detected. */
185    case ALT_E_FPGA_CRC:
186
187    /*! An error occurred on the FPGA configuration bitstream input source. */
188    case ALT_E_FPGA_CFG_STM:
189      eno = EPROTO; /* Protocol error */
190      break;
191
192    /*! The FPGA is powered off. */
193    case ALT_E_FPGA_PWR_OFF:
194      eno = ENODEV; /* No such device */
195      break;
196
197    /*! The SoC does not currently control the FPGA. */
198    case ALT_E_FPGA_NO_SOC_CTRL:
199
200    /*! The FPGA is not in USER mode. */
201    case ALT_E_FPGA_NOT_USER_MODE:
202      eno = EPERM; /* Not super-user */
203      break;
204
205    /*! An argument violates a range constraint. */
206    case ALT_E_ARG_RANGE:
207
208    /*! A bad argument value was passed. */
209    case ALT_E_BAD_ARG:
210
211    /*! The argument value is reserved or unavailable. */
212    case ALT_E_RESERVED:
213      eno = EINVAL; /* Invalid argument */
214      break;
215
216    /*! The operation is invalid or illegal. */
217    case ALT_E_BAD_OPERATION:
218
219    /*! The version ID is invalid. */
220    case ALT_E_BAD_VERSION:
221      eno = EFAULT; /* Bad address */
222      break;
223
224    /*! An operation or response timeout period expired. */
225    case ALT_E_TMO:
226
227    /*! A clock is not enabled or violates an operational constraint. */
228    case ALT_E_BAD_CLK:
229      eno = ETIME; /* Timer expired */
230      break;
231
232    /*! The buffer does not contain enough free space for the operation. */
233    case ALT_E_BUF_OVF:
234      eno = ENOMEM; /* Not enough core */
235      break;
236    default:
237
238      /* Unknown case. Implement it! */
239      assert( 0 == 1 );
240      eno = EIO;
241      break;
242  }
243
244  return eno;
245}
246
247/** @brief PHY check chip ID
248 *
249 * Check the chip-ID of the KSZ8842 ethernet PHY.
250 * @param context Address of the network context
251 * @retval  0 if chip could be identified, error code from errno.h otherwise.
252 */
253static int network_if_phy_check_chip_id( network_if_context *context )
254{
255  int      eno;
256  uint16_t reg;
257
258  eno = dwmac_if_read_from_phy(
259    context->driver_context,
260    2,
261    &reg
262    );
263
264  if ( eno == 0 ) {
265    if ( reg != 0x0022 ) { /* PHY ID part 1 */
266      eno = ENXIO;
267    } else {
268      eno = dwmac_if_read_from_phy(
269        context->driver_context,
270        3,
271        &reg
272        );
273
274      if ( eno == 0 ) {
275        /* PHY ID part 2  and
276         * Manufacturers model number */
277        if ( ( ( reg & 0xFC00 ) >> 10 ) != 0x0005 ) {
278          eno = ENXIO;
279        } else if ( ( ( reg & 0x03F0 ) >> 4 ) != 0x0021 ) {
280          eno = ENXIO;
281        }
282      }
283    }
284  }
285
286  return eno;
287}
288
289/******************************************************************************
290* Callback Methods
291******************************************************************************/
292
293/** @brief NIC enable
294 *
295 * Enables (powers up) the network interface card.
296 * @param[in] arg         The void pointer argument passed to
297 *                        dwmac_network_if_attach_detach.
298 * @retval 0 on success, error code from errno.h on failure.
299 */
300static int network_if_nic_enable( void *arg )
301{
302  int                 eno               = 0;
303  network_if_context *context           = (network_if_context *) arg;
304  ALT_CLK_t           module_clk        = ALT_CLK_EMAC0;
305  uint32_t            permodrst_clr_msk = ALT_RSTMGR_PERMODRST_EMAC0_CLR_MSK;
306
307  switch ( context->nic_id ) {
308    case NETWORK_IF_NIC_ID_EMAC0:
309    {
310      module_clk        = ALT_CLK_EMAC0;
311      permodrst_clr_msk = ALT_RSTMGR_PERMODRST_EMAC0_CLR_MSK;
312    }
313    break;
314    case NETWORK_IF_NIC_ID_EMAC1:
315    {
316      module_clk        = ALT_CLK_EMAC1;
317      permodrst_clr_msk = ALT_RSTMGR_PERMODRST_EMAC1_CLR_MSK;
318    }
319    break;
320
321    case NETWORK_IF_NIC_ID_COUNT:
322
323      /* Invalid case */
324      eno = ENODEV;
325      break;
326    default:
327
328      /* Unknown case */
329      eno = ENOTSUP;
330      break;
331  }
332
333  /* Pin mux configuration is handled by the Preloader. Thus no
334   * pin mux configuration here. */
335
336  /* Enable the clock for the EMAC module */
337  if ( eno == 0 ) {
338    if ( ALT_E_FALSE == alt_clk_is_enabled( module_clk ) ) {
339      ALT_STATUS_CODE status = alt_clk_clock_enable( module_clk );
340      eno = network_if_altera_status_code_to_errno( status );
341    }
342  }
343
344  /* Finish reset for the EMAC module */
345  if ( eno == 0 ) {
346    uint32_t reg = alt_read_word( ALT_RSTMGR_PERMODRST_ADDR );
347    alt_write_word( ALT_RSTMGR_PERMODRST_ADDR, reg & permodrst_clr_msk );
348  }
349
350  return eno;
351}
352
353/** @brief NIC disable.
354 *
355 * Disables (powers down) the network interface card.
356 * @param[in] arg         The void pointer argument passed to
357 *                        dwmac_network_if_attach_detach.
358 * @retval 0 on success, error code from errno.h on failure.
359 */
360static int network_if_nic_disable( void *arg )
361{
362  int                 eno               = 0;
363  network_if_context *context           = (network_if_context *) arg;
364  ALT_CLK_t           module_clk        = ALT_CLK_EMAC0;
365  uint32_t            permodrst_set_msk = ALT_RSTMGR_PERMODRST_EMAC0_SET_MSK;
366
367  switch ( context->nic_id ) {
368    case NETWORK_IF_NIC_ID_EMAC0:
369      module_clk        = ALT_CLK_EMAC0;
370      permodrst_set_msk = ALT_RSTMGR_PERMODRST_EMAC0_SET_MSK;
371      break;
372    case NETWORK_IF_NIC_ID_EMAC1:
373      module_clk        = ALT_CLK_EMAC1;
374      permodrst_set_msk = ALT_RSTMGR_PERMODRST_EMAC1_SET_MSK;
375      break;
376
377    case NETWORK_IF_NIC_ID_COUNT:
378
379      /* Invalid case */
380      eno = ENODEV;
381      break;
382    default:
383
384      /* Unknown case */
385      eno = ENOTSUP;
386      break;
387  }
388
389  /* Enter rest status for the EMAC module */
390  if ( eno == 0 ) {
391    uint32_t reg = alt_read_word( ALT_RSTMGR_PERMODRST_ADDR );
392    alt_write_word( ALT_RSTMGR_PERMODRST_ADDR, reg | permodrst_set_msk );
393  }
394
395  /* Disable the clock for the EMAC module */
396  if ( eno == 0 ) {
397    if ( ALT_E_TRUE == alt_clk_is_enabled( module_clk ) ) {
398      ALT_STATUS_CODE status = alt_clk_clock_disable( module_clk );
399      eno = network_if_altera_status_code_to_errno( status );
400    }
401  }
402
403  return eno;
404}
405
406/** @brief PHY disable.
407 *
408 * Disables (powers down) the network PHY.
409 * @param[in] arg         The void pointer argument passed to
410 *                        dwmac_network_if_attach_detach.
411 * @retval 0 on success, error code from errno.h on failure.
412 */
413static int network_if_phy_disable( void *arg )
414{
415  int                 eno  = 0;
416  network_if_context *self = (network_if_context *) arg;
417  uint32_t            mask;
418  ALT_STATUS_CODE     status_code;
419
420  /* The power pin of the PHY is hard wire to board power control and
421   * the PHY supports interrupts, This means we can not power it down
422   * and we don't need to stop a timer for polling PHY events. */
423
424  status_code = alt_gpio_port_int_mask_set(
425    ALT_GPIO_PORTB,
426    NETWORK_PIN_MASK_PHY_INTERRUPT,
427    NETWORK_PIN_MASK_PHY_INTERRUPT
428    );
429  eno = network_if_altera_status_code_to_errno( status_code );
430
431  if ( eno == 0 ) {
432    mask        = alt_gpio_port_int_enable_get( ALT_GPIO_PORTB );
433    status_code = alt_gpio_port_int_enable(
434      ALT_GPIO_PORTB,
435      mask & ~NETWORK_PIN_MASK_PHY_INTERRUPT
436      );
437    eno = network_if_altera_status_code_to_errno( status_code );
438  }
439
440  if ( eno == 0 ) {
441    status_code = alt_gpio_port_int_status_clear(
442      ALT_GPIO_PORTB,
443      NETWORK_PIN_MASK_PHY_INTERRUPT
444      );
445    eno = network_if_altera_status_code_to_errno( status_code );
446  }
447
448  if ( eno == 0 ) {
449    self->phy.status = NETWORK_IF_PHY_STATUS_OFF;
450  }
451
452  return eno;
453}
454
455/** @brief PHY enable.
456 *
457 * Enables (powers up) the network PHY.
458 * @param[in] arg         The void pointer argument passed to
459 *                        dwmac_network_if_attach_detach.
460 * @retval 0 on success, error code from errno.h on failure.
461 */
462static int network_if_phy_enable( void *arg )
463{
464  int                 eno  = 0;
465  network_if_context *self = (network_if_context *) arg;
466  ALT_STATUS_CODE     status_code;
467
468  /* The power pin of the PHY is hard wire to board power control and
469   * the PHY supports interrupts, This means we can not power it up
470   * and we don't need to start a timer for polling PHY events. */
471
472  eno = network_if_phy_check_chip_id( self );
473
474  if ( eno == 0 ) {
475    /* The phy is already enabled and we will reset it */
476    eno = network_if_phy_disable( self );
477
478    if ( eno == 0 ) {
479      rtems_task_wake_after( rtems_clock_get_ticks_per_second() / 100 );
480    }
481  } else {
482    eno = 0;
483  }
484
485  status_code = alt_gpio_port_datadir_set(
486    ALT_GPIO_PORTB,
487    NETWORK_PIN_MASK_PHY_INTERRUPT,
488    0
489    );
490  eno = network_if_altera_status_code_to_errno( status_code );
491
492  if ( eno == 0 ) {
493    status_code = alt_gpio_port_int_type_set(
494      ALT_GPIO_PORTB,
495      NETWORK_PIN_MASK_PHY_INTERRUPT,
496      0
497      );
498    eno = network_if_altera_status_code_to_errno( status_code );
499  }
500
501  if ( eno == 0 ) {
502    status_code = alt_gpio_port_int_pol_set(
503      ALT_GPIO_PORTB,
504      NETWORK_PIN_MASK_PHY_INTERRUPT,
505      1
506      );
507    eno = network_if_altera_status_code_to_errno( status_code );
508  }
509
510  if ( eno == 0 ) {
511    status_code = alt_gpio_port_debounce_set(
512      ALT_GPIO_PORTB,
513      NETWORK_PIN_MASK_PHY_INTERRUPT,
514      0
515      );
516    eno = network_if_altera_status_code_to_errno( status_code );
517  }
518
519  if ( eno == 0 ) {
520    status_code = alt_gpio_port_int_enable(
521      ALT_GPIO_PORTB,
522      NETWORK_PIN_MASK_PHY_INTERRUPT
523      );
524    eno = network_if_altera_status_code_to_errno( status_code );
525  }
526
527  if ( eno == 0 ) {
528    status_code = alt_gpio_port_int_mask_set(
529      ALT_GPIO_PORTB,
530      NETWORK_PIN_MASK_PHY_INTERRUPT,
531      0
532      );
533    eno = network_if_altera_status_code_to_errno( status_code );
534  }
535
536  if ( eno == 0 ) {
537    status_code = alt_gpio_port_int_status_clear(
538      ALT_GPIO_PORTB,
539      NETWORK_PIN_MASK_PHY_INTERRUPT
540      );
541    eno = network_if_altera_status_code_to_errno( status_code );
542  }
543
544  if ( eno == 0 ) {
545    self->phy.status = NETWORK_IF_PHY_STATUS_STOPPED;
546  }
547
548  return eno;
549}
550
551/** @brief Clear phy event status.
552 *
553 * Clears all PHY event statuses.
554 * @param[in] arg         The void pointer argument passed to
555 *                        dwmac_network_if_attach_detach.
556 * @retval 0 on success, error code from errno.h on failure.
557 */
558static int network_if_phy_event_status_clear( void *arg )
559{
560  int             eno;
561  ALT_STATUS_CODE status_code;
562
563  (void) arg;
564
565  /* Clear the interrupt status */
566  status_code = alt_gpio_port_int_status_clear(
567    ALT_GPIO_PORTB,
568    NETWORK_PIN_MASK_PHY_INTERRUPT
569    );
570  eno = network_if_altera_status_code_to_errno( status_code );
571
572  if ( eno == 0 ) {
573    /* Unmask the interrupt (was masked within interrupt handler) */
574    status_code = alt_gpio_port_int_mask_set(
575      ALT_GPIO_PORTB,
576      NETWORK_PIN_MASK_PHY_INTERRUPT,
577      0
578      );
579    eno = network_if_altera_status_code_to_errno( status_code );
580  }
581
582  return eno;
583}
584
585/**
586 * @brief Get the PHY event status.
587 *
588 * Reports status on PHY events (e.g. PHY interrupts).
589 * @param[in]  arg        The void pointer argument passed to
590 *                        dwmac_network_if_attach_detach.
591 * @param[out] event_set  Pointer to a buffer for a set of events for which a
592 *                        PHY status change was detected.
593 *                        For events see @see dwmac_phy_event.
594 * @retval 0 on success, error code from errno.h on failure.
595 */
596static int network_if_phy_events_status_get(
597  void            *arg,
598  dwmac_phy_event *event_set )
599{
600  int                 eno  = 0;
601  uint16_t            reg  = 0;
602  network_if_context *self = (network_if_context *) arg;
603
604  eno = dwmac_if_read_from_phy(
605    self->driver_context,
606    0x1B,
607    &reg
608    );
609
610  if ( eno == 0 ) {
611    if ( ( reg & 0x80 ) != 0 ) {
612      *event_set |= PHY_EVENT_JABBER;
613    }
614
615    if ( ( reg & 0x40 ) != 0 ) {
616      *event_set |= PHY_EVENT_RECEIVE_ERROR;
617    }
618
619    if ( ( reg & 0x20 ) != 0 ) {
620      *event_set |= PHY_EVENT_PAGE_RECEIVE;
621    }
622
623    if ( ( reg & 0x10 ) != 0 ) {
624      *event_set |= PHY_EVENT_PARALLEL_DETECT_FAULT;
625    }
626
627    if ( ( reg & 0x08 ) != 0 ) {
628      *event_set |= PHY_EVENT_LINK_PARTNER_ACK;
629    }
630
631    if ( ( reg & 0x04 ) != 0 ) {
632      *event_set |= PHY_EVENT_LINK_DOWN;
633    }
634
635    if ( ( reg & 0x02 ) != 0 ) {
636      *event_set |= PHY_EVENT_REMOTE_FAULT;
637    }
638
639    if ( ( reg & 0x01 ) != 0 ) {
640      *event_set |= PHY_EVENT_LINK_UP;
641    }
642  }
643
644  return eno;
645}
646
647/** @brief Network PHY interrupt handler
648 *
649 * Handling method for interrupts from the PHY.
650 * @param arg Address of the network interface context
651 */
652static void network_if_phy_interrupt_handler( void *arg )
653{
654  int                 eno  = 0;
655  network_if_context *self = (network_if_context *) arg;
656  uint32_t            reg;
657
658  reg = alt_gpio_port_int_status_get( ALT_GPIO_PORTB );
659
660  if ( reg & NETWORK_PIN_MASK_PHY_INTERRUPT ) {
661    /* We have a level interrupt and we expect the driver to do
662     * the actual interrupt handling from within a task context.
663     * Thus we have to mask the interrupt. A call to
664     * network_if_phy_event_status_clear() will unmask it */
665    ALT_STATUS_CODE status_code = alt_gpio_port_int_mask_set(
666      ALT_GPIO_PORTB,
667      NETWORK_PIN_MASK_PHY_INTERRUPT,
668      NETWORK_PIN_MASK_PHY_INTERRUPT
669      );
670    eno = network_if_altera_status_code_to_errno( status_code );
671
672    if ( eno == 0 ) {
673      eno = dwmac_if_handle_phy_event(
674        self->driver_context
675        );
676    }
677  }
678
679  assert( eno == 0 );
680}
681
682/** @brief PHY event enable.
683 *
684 * Enables generation of events for an event set.
685 * @param[in] arg         The void pointer argument passed to
686 *                        dwmac_network_if_attach_detach.
687 * @param[in] event_set   Set of events. For these events shall get generated
688 *                        upon PHY status change.
689 * @retval 0 on success, error code from errno.h on failure.
690 */
691static int network_if_phy_event_enable(
692  void                 *arg,
693  const dwmac_phy_event event_set )
694{
695  int                   eno        = 0;
696  uint16_t              reg        = 0;
697  network_if_context   *self       = (network_if_context *) arg;
698  network_if_phy_status old_status = self->phy.status;
699
700  if ( arg != NULL ) {
701    switch ( self->phy.status ) {
702      case NETWORK_IF_PHY_STATUS_OFF:
703        break;
704      case NETWORK_IF_PHY_STATUS_STOPPED:
705        break;
706      case NETWORK_IF_PHY_STATUS_STARTED:
707      {
708        eno = dwmac_if_read_from_phy(
709          self->driver_context,
710          0x1B,
711          &reg
712          );
713
714        if ( eno == 0 ) {
715          /* Disable all interrupts, but leave interrupt status
716           * bits untouched */
717          reg &= 0x00FF;
718          eno  = dwmac_if_write_to_phy(
719            self->driver_context,
720            0x1B,
721            reg
722            );
723        }
724
725        if ( eno == 0 ) {
726          self->phy.irq_mask = 0;
727          self->phy.status   = NETWORK_IF_PHY_STATUS_STOPPED;
728        }
729      }
730      break;
731      case NETWORK_IF_PHY_STATUS_COUNT:
732      {
733        /* Invalid case */
734        assert( self->phy.status != NETWORK_IF_PHY_STATUS_COUNT );
735        eno = ENOTSUP;
736      }
737      break;
738      default:
739      {
740        /* Unknown case */
741        assert(
742          self->phy.status == NETWORK_IF_PHY_STATUS_OFF
743          || self->phy.status == NETWORK_IF_PHY_STATUS_STOPPED
744          || self->phy.status == NETWORK_IF_PHY_STATUS_STARTED
745          );
746        eno = ENOTSUP;
747      }
748      break;
749    }
750
751    if ( eno == 0 ) {
752      /* Select interrupts to enable */
753      if ( ( event_set & PHY_EVENT_JABBER ) != 0 ) {
754        reg |= 0x8000;
755      }
756
757      if ( ( event_set & PHY_EVENT_RECEIVE_ERROR ) != 0 ) {
758        reg |= 0x4000;
759      }
760
761      if ( ( event_set & PHY_EVENT_PAGE_RECEIVE ) != 0 ) {
762        reg |= 0x2000;
763      }
764
765      if ( ( event_set & PHY_EVENT_PARALLEL_DETECT_FAULT ) != 0 ) {
766        reg |= 0x1000;
767      }
768
769      if ( ( event_set & PHY_EVENT_LINK_PARTNER_ACK ) != 0 ) {
770        reg |= 0x0800;
771      }
772
773      if ( ( event_set & PHY_EVENT_LINK_DOWN ) != 0 ) {
774        reg |= 0x0400;
775      }
776
777      if ( ( event_set & PHY_EVENT_REMOTE_FAULT ) != 0 ) {
778        reg |= 0x0200;
779      }
780
781      if ( ( event_set & PHY_EVENT_LINK_UP ) != 0 ) {
782        reg |= 0x100;
783      }
784
785      switch ( old_status ) {
786        case NETWORK_IF_PHY_STATUS_OFF:
787          break;
788        case NETWORK_IF_PHY_STATUS_STOPPED:
789          break;
790        case NETWORK_IF_PHY_STATUS_STARTED:
791        {
792          if ( eno == 0 ) {
793            /* Modify interrupt enable bits, but leave
794             * interrupt status bits untouched */
795            eno = dwmac_if_write_to_phy(
796              self->driver_context,
797              0x1B,
798              reg & 0xFF00
799              );
800          }
801        }
802        break;
803        case NETWORK_IF_PHY_STATUS_COUNT:
804        {
805          /* Invalid case */
806          assert( self->phy.status != NETWORK_IF_PHY_STATUS_COUNT );
807          eno = ENOTSUP;
808        }
809        break;
810        default:
811        {
812          /* Unknown case */
813          assert(
814            self->phy.status == NETWORK_IF_PHY_STATUS_OFF
815            || self->phy.status == NETWORK_IF_PHY_STATUS_STOPPED
816            || self->phy.status == NETWORK_IF_PHY_STATUS_STARTED
817            );
818          eno = ENOTSUP;
819        }
820        break;
821      }
822
823      if ( eno == 0 ) {
824        self->phy.irq_mask = reg & 0xFF00;
825      }
826    }
827  }
828
829  return eno;
830}
831
832/**
833 * @brief Stop the network PHY.
834 *
835 * Do anything necessary to stop the network PHY (stop generating events, ...).
836 * @param[in] arg         The void pointer argument passed to
837 *                        dwmac_network_if_attach_detach.
838 * @retval 0 on success, error code from errno.h on failure.
839 */
840static int network_if_phy_stop( void *arg )
841{
842  int                 eno  = 0;
843  rtems_status_code   sc   = RTEMS_SUCCESSFUL;
844  network_if_context *self = (network_if_context *) arg;
845
846  switch ( self->phy.status ) {
847    case NETWORK_IF_PHY_STATUS_OFF:
848      break;
849    case NETWORK_IF_PHY_STATUS_STOPPED:
850      break;
851    case NETWORK_IF_PHY_STATUS_STARTED:
852    {
853      /* Remove any selected interrupts */
854      eno = dwmac_if_write_to_phy(
855        self->driver_context,
856        0x1B,
857        0
858        );
859
860      if ( eno == 0 ) {
861        uint16_t reg;
862
863        /* Clear interrupt status bits by reading them */
864        eno = dwmac_if_read_from_phy(
865          self->driver_context,
866          0x1B,
867          &reg
868          );
869      }
870
871      if ( eno == 0 ) {
872        self->phy.irq_mask = 0;
873
874        /* Remove interrupt handler */
875        sc = rtems_interrupt_handler_remove(
876          ALT_INT_INTERRUPT_GPIO1,
877          network_if_phy_interrupt_handler,
878          self
879          );
880        eno = rtems_status_code_to_errno( sc );
881
882        if ( eno == 0 ) {
883          self->phy.status = NETWORK_IF_PHY_STATUS_STOPPED;
884        }
885      }
886    }
887    break;
888    case NETWORK_IF_PHY_STATUS_COUNT:
889    {
890      /* Invalid case */
891      assert( self->phy.status != NETWORK_IF_PHY_STATUS_COUNT );
892      eno = ENOTSUP;
893    }
894    break;
895    default:
896    {
897      /* Unknown case */
898      assert(
899        self->phy.status == NETWORK_IF_PHY_STATUS_OFF
900        || self->phy.status == NETWORK_IF_PHY_STATUS_STOPPED
901        || self->phy.status == NETWORK_IF_PHY_STATUS_STARTED
902        );
903      eno = ENOTSUP;
904    }
905    break;
906  }
907
908  return eno;
909}
910
911/**
912 * @brief Start the network PHY.
913 *
914 * Do anything necessary to start the network PHY (start generating
915 * events, ...).
916 * @param[in] arg         The void pointer argument passed to
917 *                        dwmac_network_if_attach_detach.
918 * @retval 0 on success, error code from errno.h on failure.
919 */
920static int network_if_phy_start( void *arg )
921{
922  int                 eno  = 0;
923  rtems_status_code   sc   = RTEMS_SUCCESSFUL;
924  network_if_context *self = (network_if_context *) arg;
925
926  switch ( self->phy.status ) {
927    case NETWORK_IF_PHY_STATUS_OFF:
928    {
929      assert( self->phy.status != NETWORK_IF_PHY_STATUS_OFF );
930      eno = EHOSTDOWN;
931    }
932    break;
933    case NETWORK_IF_PHY_STATUS_STOPPED:
934
935      /* Disable all interrupts */
936      eno = dwmac_if_write_to_phy(
937        self->driver_context,
938        0x1B,
939        0
940        );
941
942      if ( eno == 0 ) {
943      }
944
945      if ( eno == 0 ) {
946        /* Install interrupt handler */
947        sc = rtems_interrupt_handler_install(
948          ALT_INT_INTERRUPT_GPIO1,
949          NULL,
950          RTEMS_INTERRUPT_SHARED,
951          network_if_phy_interrupt_handler,
952          self
953          );
954        eno = rtems_status_code_to_errno( sc );
955      }
956
957      if ( eno == 0 ) {
958        /* Enable interrupts */
959        eno = dwmac_if_write_to_phy(
960          self->driver_context,
961          0x1B,
962          self->phy.irq_mask
963          );
964      }
965
966      break;
967    case NETWORK_IF_PHY_STATUS_STARTED:
968    {
969      uint16_t irq_mask = self->phy.irq_mask;
970
971      /* Re-start the phy */
972      eno = network_if_phy_stop( self );
973
974      if ( eno == 0 ) {
975        self->phy.irq_mask = irq_mask;
976
977        eno                = network_if_phy_start(
978          self
979          );
980      }
981    }
982    break;
983
984    case NETWORK_IF_PHY_STATUS_COUNT:
985    {
986      /* Invalid case */
987      assert( self->phy.status != NETWORK_IF_PHY_STATUS_COUNT );
988      eno = ENOTSUP;
989    }
990    break;
991    default:
992    {
993      /* Unknown case */
994      assert(
995        self->phy.status == NETWORK_IF_PHY_STATUS_OFF
996        || self->phy.status == NETWORK_IF_PHY_STATUS_STOPPED
997        || self->phy.status == NETWORK_IF_PHY_STATUS_STARTED
998        );
999      eno = ENOTSUP;
1000    }
1001    break;
1002  }
1003
1004  if ( eno == 0 ) {
1005    self->phy.status = NETWORK_IF_PHY_STATUS_STARTED;
1006  }
1007
1008  return eno;
1009}
1010
1011/**
1012 * @brief Allocate nocache RAM.
1013 *
1014 * Allocate uncached RAM.
1015 * @param[in]  arg        The void pointer argument passed to
1016 *                        dwmac_network_if_attach_detach.
1017 * @param[out] memory     Pointer of a buffer to write the address of the
1018 *                        allocated memory to.
1019 * @param[in]  size       Number of bytes to be allocated
1020 * @retval 0 on success, error code from errno.h on failure.
1021 */
1022static int network_if_mem_alloc_nocache(
1023  void        *arg,
1024  void       **memory,
1025  const size_t size )
1026{
1027  int eno = EINVAL;
1028
1029  (void) arg;
1030
1031  assert( memory != NULL );
1032
1033  if ( memory != NULL ) {
1034    *memory = rtems_cache_coherent_allocate( size, 0, 0 );
1035
1036    if ( *memory != NULL ) {
1037      eno = 0;
1038    } else {
1039      eno = ENOMEM;
1040    }
1041  }
1042
1043  return eno;
1044}
1045
1046/**
1047 * @brief Free nocache RAM.
1048 *
1049 * Release uncached RAM.
1050 * @param[in] arg         The void pointer argument passed to
1051 *                        dwmac_network_if_attach_detach.
1052 * @param[in] memory      Pointer to the memory to be freed.
1053 * @retval 0 on success, error code from errno.h on failure.
1054 */
1055static int network_if_mem_free_nocache(
1056  void *arg,
1057  void *memory )
1058{
1059  (void) arg;
1060
1061  rtems_cache_coherent_free( memory );
1062
1063  return 0;
1064}
1065
1066/**
1067 * @brief Bus setup.
1068 *
1069 * Callback method for setting up the system bus for the network driver.
1070 * @param[in] arg         The void pointer argument passed to
1071 *                        dwmac_network_if_attach_detach.
1072 * @retval 0 on success, error code from errno.h on failure.
1073 */
1074static int network_if_bus_setup( void *arg )
1075{
1076  volatile uint32_t reg = alt_read_word( ALT_SYSMGR_EMAC_L3MST_ADDR );
1077
1078  (void) arg;
1079
1080  reg &= ALT_SYSMGR_EMAC_L3MST_AWCACHE_1_CLR_MSK;
1081  reg &= ALT_SYSMGR_EMAC_L3MST_ARCACHE_1_CLR_MSK;
1082  reg |= ALT_SYSMGR_EMAC_L3MST_AWCACHE_1_SET(
1083    ALT_SYSMGR_EMAC_L3MST_AWCACHE_1_E_CACHE_WRBACK_ALLOC
1084  );
1085  reg |= ALT_SYSMGR_EMAC_L3MST_ARCACHE_1_SET(
1086    ALT_SYSMGR_EMAC_L3MST_ARCACHE_1_E_CACHE_WRBACK_ALLOC
1087  );
1088
1089  alt_write_word( ALT_SYSMGR_EMAC_L3MST_ADDR, reg );
1090
1091  return 0;
1092}
1093
1094/*****************************************************************************
1095* Context Data
1096******************************************************************************/
1097
1098/** @brief Network interface context */
1099static network_if_context context = NETWORK_IF_CONTEXT_INITIALIZER(
1100
1101  /** @brief Use EMAC1 */
1102  NETWORK_IF_NIC_ID_EMAC1,
1103
1104  /** @brief Address of driver context
1105   *
1106   * To be assigned with return value of dwmac_network_if_attach_detach */
1107  NULL
1108  );
1109
1110/** @brief Network driver DMA configuration data */
1111static const dwmac_dma_cfg NETWORK_DMA_CFG = DWMAC_DMA_CFG_INITIALIZER(
1112
1113  /** @brief Bus mode burst length */
1114  DWMAC_DMA_CFG_BUS_MODE_BURST_LENGTH_64,
1115
1116  /** @brief Bus mode fixed burst? */
1117  DWMAC_DMA_CFG_BUS_MODE_BURST_MODE_SINGLE_OR_INCR,
1118
1119  /** @brief Bus mode mixed burst? */
1120  DWMAC_DMA_CFG_BUS_MODE_BURST_NOT_MIXED,
1121
1122  /** @brief Permit AXI burst length of 4 */
1123  DWMAC_DMA_CFG_AXI_BURST_LENGTH_4_NOT_SUPPORTED,
1124
1125  /** @brief Permit AXI burst length of 8 */
1126  DWMAC_DMA_CFG_AXI_BURST_LENGTH_8_NOT_SUPPORTED,
1127
1128  /** @brief Permit AXI burst length of 16 */
1129  DWMAC_DMA_CFG_AXI_BURST_LENGTH_16_NOT_SUPPORTED,
1130
1131  /** @brief Select boundary crossing mode */
1132  DWMAC_DMA_CFG_AXI_BURST_BOUNDARY_4_KB
1133  );
1134
1135/** @brief Network driver configuration data */
1136static const dwmac_cfg NETWORK_DRIVER_CFG = DWMAC_CFG_INITIALIZER(
1137
1138  /** @brief GMII clock rate */
1139  75000000,
1140
1141  /** @brief Address of the GMAC registers for emac1 */
1142  ALT_EMAC1_GMACGRP_ADDR,
1143
1144  /** @brief Address of the DMA registers for emac1 */
1145  ALT_EMAC1_DMAGRP_ADDR,
1146
1147  /** @brief Address of the PHY on the mdio bus */
1148  4,
1149
1150  /** @brief Bytes per L1 cache line */
1151  32,
1152
1153  /** @brief Interrupt number for the EMAC */
1154  ALT_INT_INTERRUPT_EMAC1_IRQ,
1155
1156  /** @brief DMA configuration */
1157  &NETWORK_DMA_CFG,
1158
1159  /** @brief Callback method for enabling the network controller */
1160  network_if_nic_enable,
1161
1162  /** @brief Callback method for disabling the network controller */
1163  network_if_nic_disable,
1164
1165  /** @brief Callback method for enabling the PHY */
1166  network_if_phy_enable,
1167
1168  /** @brief Callback method for disabling the PHY */
1169  network_if_phy_disable,
1170
1171  /** @brief Callback which enables PHY events for forwarding to driver */
1172  network_if_phy_event_enable,
1173
1174  /** @brief Callback method for clearing PHY interrupt status */
1175  network_if_phy_event_status_clear,
1176
1177  /** @brief Callback method for getting a set of events from the PHY */
1178  network_if_phy_events_status_get,
1179
1180  /** @brief Callback method for making the PHY start forwarding events */
1181  network_if_phy_start,
1182
1183  /** @brief Callback method for making the PHY stop forwarding events */
1184  network_if_phy_stop,
1185
1186  /** @brief Callback method for allocating uncached memory */
1187  network_if_mem_alloc_nocache,
1188
1189  /** @brief Callback method for freeing uncached memory */
1190  network_if_mem_free_nocache,
1191
1192  /** @brief Callback method for setting up bus upon driver startup */
1193  network_if_bus_setup,
1194
1195  /** @brief Operations for the ethernet mac 1000 */
1196  &DWMAC_1000_ETHERNET_MAC_OPS,
1197
1198  /** @brief Operations for the enhanced DMA descriptors */
1199  &DWMAC_DESCRIPTOR_OPS_ENHANCED
1200  );
1201
1202/******************************************************************************
1203* API
1204******************************************************************************/
1205
1206/** @brief Network interface attach/detach
1207 *
1208 * The standard attach/detach method for network interfaces.
1209 * This methods will call the DWMAC 1000 attach/detach method and pass
1210 * configuration data and callback methods into it. Via these  the general
1211 * DWMAC 1000 driver is able the handle micro controller specific things.
1212 * NOTE: Detaching is NOT supported!
1213 * @param config    Configuration parameters from and for the BSD network
1214 *                  stack.
1215 * @param attaching True if attaching the driver to the network stack, false if
1216 *                  detaching.
1217 * @retval 0 if successful, error code from errno.h if not.
1218 */
1219int altera_cyclone_v_network_if_attach_detach(
1220  struct rtems_bsdnet_ifconfig *config,
1221  int                           attaching )
1222{
1223  int eno = 0;
1224
1225  assert( config != NULL );
1226
1227  /* Network controller initialization */
1228  context.driver_context = dwmac_network_if_attach_detach(
1229    config,
1230    &NETWORK_DRIVER_CFG,
1231    &context,
1232    attaching
1233    );
1234
1235  if ( context.driver_context == NULL ) {
1236    eno = EFAULT;
1237  }
1238
1239  return eno;
1240}
1241
1242#endif /* defined(RTEMS_NETWORKING) */
Note: See TracBrowser for help on using the repository browser.