/*
 * FILENAME: phy_dp83848.c
 *
 * Copyright 1997-2012 By InterNiche Technologies Inc. All rights reserved
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation and other 
 * materials related to such distribution and use acknowledge that 
 * the software was developed by the University of California, Berkeley.
 * The name of the University may not be used to endorse or promote 
 * products derived from this software without specific prior written 
 * permission. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *
 * Nat'l Semiconductor DP83848C PHY support for the LPC17xx Ethernet MAC driver
 *
 * ROUTINES: emac_phy_init()
 *
 * PORTABLE: no
 */

#include <stdint.h>     /* C compiler files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "embtcp.h"

#include "phy_dp83848.h"


/* FUNCTION: usleep()
 *
 * Insert execution delay.
 * 'count' is approximately the number of microseconds to delay.
 */
void
usleep(int count) 
{
   volatile uint32_t i;

   while (--count >= 0)
   {
      for (i = 0; i < 100; i++)
      {
         /* null */ ;
      }
   }
}



/* FUNCTION: emac_phy_init()
 *
 * Initialize the PHY chip
 *
 * PARAMS: none
 *
 * RETURNS: 0 if successful, otherwise non-zero error code
 */
int
emac_phy_init(void)
{
   uint16_t phy_reg, phy_reg2;
   int count;
   int speed;
   bool_t duplex;

   /* reset the PHY */
   emac_phy_write(EMAC_PHY_ADDR, PHY_BMCR, BMCR_RESET);
   usleep(3);                 /* wait 3 usec */
   do
   {
      phy_reg = emac_phy_read(EMAC_PHY_ADDR, PHY_BMCR);
   } while (phy_reg & BMCR_RESET);
   
   /* validate PHY ID */
   phy_reg  = emac_phy_read(EMAC_PHY_ADDR, PHY_PHYIDR1);
   phy_reg2 = emac_phy_read(EMAC_PHY_ADDR, PHY_PHYIDR2);

   if ((phy_reg  != OUI_MSB) ||
       (phy_reg2 != (OUI_LSB | VNDR_MDL | MDL_REV)))
   {
      return (-3);
   }

   /* start auto-negotiation of the PHY LINK */
   {
      emac_phy_write(EMAC_PHY_ADDR, PHY_BMCR, 0);      /* clear BMCR_AN */
      emac_phy_write(EMAC_PHY_ADDR, PHY_BMCR, BMCR_AN | BMCR_RE_AN);

      count = 30;
      do
      {
         usleep(100000);      /* 0.1 seconds */
         phy_reg = emac_phy_read(EMAC_PHY_ADDR, PHY_PHYSTS);   /* check status */
      } while (((phy_reg & PHYSTS_AN_DONE) == 0) && (--count >= 0));


       /* print the results */
       printf("LPC17xx Auto-negotiation: ");
       if (count < 0)
       {
           printf("ERROR; timed out\n");
           return (-1);
       }
       if (phy_reg & PHYSTS_REM_FAULT)
       {
           printf("ERROR; remote fault\n");
           return (-1);
       }
       if ((phy_reg & PHYSTS_LINK) == 0)
       {
           printf("ERROR; link fail\n");
           return (-1);
       }
   }

   phy_reg2 = emac_phy_read(EMAC_PHY_ADDR, PHY_BMCR);
   phy_reg2 &= ~(BMCR_AN | BMCR_RE_AN);
   if (!(phy_reg & PHYSTS_SPEED))
   {
      phy_reg2 |= BMCR_SPEED;
      speed = 100;
      printf("100 Mbps, ");
   }
   else
   {
      phy_reg2 &= ~BMCR_SPEED;
      speed = 10;
      printf("10 Mbps, ");
   }

   if (phy_reg & PHYSTS_DUPLEX)
   {
      phy_reg2      |= BMCR_DUPLEX;
      duplex = TRUE;
      printf("Full Duplex\n");
   }
   else
   {
      phy_reg2      &= ~BMCR_DUPLEX;
      duplex = FALSE;
      printf("Half Duplex\n");
   }

   /* update link info */
   emac_phy_write(EMAC_PHY_ADDR, PHY_BMCR, phy_reg2);
   eth_setlink(EMAC_PHY_ADDR, speed, duplex);

   return (0);
}



/***************************************************************************
 *
 * NicheLib memory management routines. 
 *
 * npalloc(), npfree(), and nprealloc() allocate and free memory
 * from the heap. These functions are implemented as wrappers 
 * around the FreeRTOS memory allocate and free functions.
 *
 * ahb_alloc() and ahb_free() allocate and free memory from the AHB SRAM. 
 * Structures allocated from this region are assumed to be allocated
 * once and never freed. For example, packet buffers, network interface
 * structures, and task stacks. If structures from this region are
 * required to be freed and/or reallocated, more robust memory
 * management functions can be implemented by the user.
 *
 ***************************************************************************/

/* FUNCTION: npalloc()
 *
 * Allocate memory for NicheStack.
 * Allocation size is rounded up to a multiple of 32-bits.
 * The allocated memory is initialized to zeros.
 *
 * PARAM1: uint32_t           # of bytes to allocate
 *
 * RETURN: char *             ptr to allocated memory or NULL
 */
void *
npalloc(uint32_t bytes)
{
   void *ptr = pvPortMalloc((bytes + 3) & ~0x3);

   if (ptr)
   {
      memset(ptr, 0, bytes);
   }

   return (ptr);
}



/* FUNCTION: npfree()
 *
 * Free an allocated block of memory.
 *
 * PARAM1: void *             ptr to block of memory
 *
 * RETURN: none
 */
void
npfree(void *ptr)
{
   vPortFree(ptr);
}



/* FUNCTION: nprealloc()
 *
 * Reallocate a block of memory.
 *
 * PARAM1: void *             ptr to old block of memory
 * PARAM2: size_t             size of new block (bytes)
 *
 * RETURN: void *             ptr to new block of memory or NULL
 */
void *
nprealloc(void *old, size_t bytes)
{
   void *ptr = NULL;
  
   if (bytes > 0)
   {
      ptr = npalloc((uint32_t)bytes);
   }

   if (old && ptr && (bytes > 0))
   {
      memcpy(ptr, old, bytes);
   }

   if (old)
      npfree(old);

   return (ptr);
}



/* FUNCTION: ahb_alloc()
 *
 * Allocate a block of memory aligned to a 8-byte boundary.
 * Memory is allocated from AHB SRAM. This implementation 
 * assumes that allocated memory is not freed.
 *
 * PARAM1: uint32_t           number of bytes to allocate
 *
 * RETURN: void *             ptr to memory or NULL
 */
void *
ahb_alloc(uint32_t bytes)
{
   void *ptr = NULL;
   static uint8_t *ahb_sram = (uint8_t *)0x2007c000;
   static uint32_t ahb_size = 0x8000;

   bytes = (bytes + 7) & ~0x7;

   if (bytes <= ahb_size)
   {
      ptr = (void *)ahb_sram;
      ahb_sram += bytes;
      ahb_size -= bytes;
      memset(ptr, 0, bytes);
   }

   return (ptr);
}



/* FUNCTION: ahb_free()
 *
 * Free a block of AHB SRAM memory.
 *
 * PARAM1: void *             ptr to memory
 *
 * RETURN: none
 */
void
ahb_free(void *ptr)
{
   /* This implementation assumes that allocated AHB SRAM 
    * memory is not freed, so just notify the user.
    */
   printf("ahb_free. ptr = %p\n", ptr);
}

