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

4.115
Last change on this file since 32c8960 was 32c8960, checked in by Ralf Kirchner <ralf.kirchner@…>, on 05/28/14 at 12:47:03

bsp/altera-cyclone-v: Enable L2 cache for network driver

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