/******************************************************************************/
/*                       Copyright 2001 Broadcom Corporation                  */
/*                                                                            */
/* This material is the confidential trade secret and proprietary information */
/* of Broadcom Corp.  It may not be reproduced, used, sold or transferred to  */
/* any third party without the prior written consent of Broadcom Corp.        */
/* All rights reserved.                                                       */
/******************************************************************************/
/*   FileName        : bcm_boot.c                                             */
/*   Purpose         : boot of BCM6410                                        */
/*                                                                            */
/*         In order to boot the BCM6410, the following steps need to be       */
/*         achieved:                                                          */
/*                                                                            */
/*         1. install the booter                                              */
/*            - reset the BCM6410:                                            */
/*              + if the BCM6410 is equipped with a serial PROM,              */
/*                the booter will be automatically downloaded and started     */
/*              + if the BCM6410 is not equipped with a serial PROM,          */
/*                the booter image must be copied in the FMEM and the BCM6410 */
/*                must be started                                             */
/*            - wait the booter is ready to handle the download protocol      */
/*              + the BCM6410 booter generate an interrupt when it is ready   */
/*                                                                            */
/*         2. init HMI stack                                                  */
/*                                                                            */
/*         3. download the firmware image                                     */
/*            - send the generate boot request                                */
/*            - send the bootPackets                                          */
/*            - send the goOperational request                                */
/*                                                                            */
/*         4. wait the firmware is ready                                      */
/*            - when the firmware is ready, it generates an interrupt         */
/*                                                                            */
/*         5. init HMI stack                                                  */
/*                                                                            */
/*   Limitations     : None                                                   */
/*   Author          : Emmanuel Hanssens                                      */
/*   Creation Date   : 17-Sep-2001                                            */
/*   History         : Compiled by CVS                                        */
/******************************************************************************/
/*   $Id: bcm_boot.c,v 1.18 2004/09/27 15:33:13 hanssens Exp $  */
/******************************************************************************/

/* booting is done via bootp protocol for ipdslam */
#ifndef UDP_PHY

# include <stdio.h>
# include <stdlib.h>
# include <string.h>

# include "bcm_boot.h"
# include "bcm_errorHandler.h"
# ifndef NO_HOST_ACCESS
#  include "bcm_hostFunctions.h"
#  include "bcm_interrupt.h"
# endif
# include "bcm_hmiBootServices.h"
# include "bcm_timer.h"

/* {{{ private functions declarations */

# ifndef NO_HOST_ACCESS
static int installBooter(HostDev *dev, TargetImage *bootImage);
# endif
static int downloadFirmwareImage(HmiDev      *hmi,
                                 TargetImage *fwImage);

/* }}} */
/* {{{ boot: boot the specified device */

int boot(TargetImage *bootImage,
         TargetImage *fwImage,
         int          bcm6410Id,
         SelfTest    *selfTest)
{
  HmiDev  *hmiDev;
  int      res;
  
# ifndef NO_HOST_ACCESS

  HostDev *hostDev;
    
  /* init host accesses */
  hostDev = hostOpen(bcm6410Id);
  hostInit(hostDev);
  
  /* first prepare for boot */
  res = installBooter(hostDev, bootImage);

  /* get self test results */
  hostGetSelfTest(hostDev, selfTest);

  hostClose(hostDev);
  
  if (res != BCM_SUCCESS)
    return BCM_BOOTER_NO_START;
  
#  ifndef BCM_NO_SELFTEST_CHECK
  if ((selfTest->progress | selfTest->results) != 0)
    return BCM_BOOTER_POST_FAILED;
#  endif

# else
  BCM_UNUSED_PARAMETER(bootImage);
  BCM_UNUSED_PARAMETER(selfTest->progress);
# endif
  
  /* init hmi protocol */
  hmiDev = hmiOpen(bcm6410Id);
  hmiInit(hmiDev);
  
  res = downloadFirmwareImage(hmiDev, fwImage);
  
  hmiClose(hmiDev);

  if (res != BCM_SUCCESS)
    return BCM_DOWNLOAD_PROBLEM;
  
# ifndef NO_HOST_ACCESS
  /* re-open the device (we closed it in case of early return) */
  hostDev = hostOpen(bcm6410Id);
  hostInit(hostDev);

  /* wait until BCM6410 target image is ready */
  res = waitForTargetReady(hostDev);

  /* read again the self test results (fw performs additional tests) */
  hostGetSelfTest(hostDev, selfTest);

  hostClose(hostDev);
  
  if (res != BCM_SUCCESS)
    return BCM_FIRMWARE_NO_START;

#  ifndef BCM_NO_SELFTEST_CHECK
  if ((selfTest->progress | selfTest->results) != 0)
    return BCM_FIRMWARE_POST_FAILED;
#  endif
# else
  /* need to wait for target to be ready, but can't poll => sleep */
  goSleep(fwReadyTimeOut);
# endif
    
  return BCM_SUCCESS;
}

/* }}} */
/* {{{ installBooter: load the booter image if necessary */

# ifndef NO_HOST_ACCESS
static int installBooter(HostDev *dev, TargetImage *bootImage)
{
  int res;
  
  /* reset the BCM6410 */
  hostResetBCM6410(dev);

  /* when there is no serial PROM on the board, booter image must be loaded */
  if (! hostPROMonBoard(dev))
  {
    /* write the image at page 0 */
    hostWriteImage(dev, 0, bootImage->image, bootImage->size);
  
    /* need to reset page to 0 for self test results, before starting booter */
    /* in the event of a booter crash at very beginning, the selftest */
    /* position is in page 0, while if the booter is larger than 1 page, */
    /* previous hostWriteImage function will have modified this */
    hostSetPage(dev, 0);
  
    /* enable BCM6410 */
    hostEnableBCM6410(dev);
  }
  
  /* wait the booter is ready */
  res = waitForBooterReady(dev);
  if (res != BCM_SUCCESS)
  {
#  ifndef NO_FULL_ERROR_HANDLING
    return SOFT_ERROR(EHOSTBTRD, hostErr[EHOSTBTRD]);
#  else
    return BCM_FAILURE;
#  endif
  }
  
  
  return BCM_SUCCESS;
}
# endif

/* }}} */
/* {{{ downloadFirmwareImage: send target image through HMI protocol */

static int downloadFirmwareImage(HmiDev      *hmi,
                                 TargetImage *fwImage)
{
  uint16                      maxSize;
  int                         res = 0;
  uint32                      reqRef;
  GenerateBootRequestMsgReq   genBootMsgReq;
  GenerateBootRequestMsgRep   genBootMsgRep;
  SendBootPacketMsgReq       *bootMsg;
  GoOperationalMsgReq         goMsgReq;
  GoOperationalMsgRep         goMsgRep;

  
  /* compute max size of the firmware image  that can be sent in each boot
     packet + allocate bootMsg */
  maxSize = hmiGetReqMsgMaxSize(hmi);
  bootMsg = (SendBootPacketMsgReq *)malloc(maxSize);
  /* forsee some place for the other boot packet fields */
  maxSize -= SIZE_OF_REQ_MSG(SendBootPacket);
  
  /* generate boot request */
  genBootMsgReq.interval = 10;
  res = GenerateBootRequest(hmi,
                            BOOT_APPLICATION_ID,
                            &genBootMsgReq, &genBootMsgRep);
	if (res != BCM_SUCCESS)
    return SOFT_ERROR(EHMISEFA, hmiErr[EHMISEFA],
                      GenerateBootRequestLayout.name, res);

# ifndef NO_STDOUT_PRINT
  /* show download protocol and booter version */
  printf("Boot firmware release: %d.%d.%d\n",
         genBootMsgRep.booterMajorVersion, genBootMsgRep.booterMinorVersion,
         genBootMsgRep.booterFixVersion);
  printf("HMI download protocol: %d.%d\n",
         genBootMsgRep.downloadProtocolMajorVersion,
         genBootMsgRep.downloadProtocolMinorVersion);

  /* start download */
  printf("Loading target image through HMI:  0%% ");
  fflush(stdout);
# endif
  
  /* send boot packets */
  bootMsg->offset = 0;
  bootMsg->imageType = genBootMsgRep.imageType;
  
  while (bootMsg->offset < fwImage->size)
  {
# ifndef NO_STDOUT_PRINT
    /* show progression */
    printf("\b\b\b\b%2ld%% ", (bootMsg->offset*100)/fwImage->size);
    fflush(stdout);
# endif
    
    /* prepare message data */
    bootMsg->packetSize = min(fwImage->size - bootMsg->offset, maxSize);
    memcpy((void *) (bootMsg + 1), /* binary is appended at the end of bootmsg*/
           fwImage->image + bootMsg->offset,
           bootMsg->packetSize);

    res = SendBootPacket(hmi, BOOT_APPLICATION_ID, bootMsg, &reqRef);
    if (res != BCM_SUCCESS)
    {
      /* release the boot msg buffer */
      free(bootMsg);
      return SOFT_ERROR(EHMISEFA, hmiErr[EHMISEFA], SendBootPacketLayout.name,
                        res);
    }
    bootMsg->offset += bootMsg->packetSize;
  }

  /* release the boot msg buffer */
  free(bootMsg);
  
# ifndef NO_STDOUT_PRINT
  printf("\b\b\b\bdone\n");
# endif

  /* send the go operational data */
  goMsgReq.imageSize         = fwImage->size;
  goMsgReq.imageType         = genBootMsgRep.imageType;
  goMsgReq.controlUtopiaAddr = -2; /* keep current setting */

  res = GoOperational(hmi, BOOT_APPLICATION_ID, &goMsgReq, &goMsgRep);
  if (res != BCM_SUCCESS)
    return SOFT_ERROR(EHMISEFA, hmiErr[EHMISEFA], GoOperationalLayout.name,
                      res);

  /* check the firmware answer to the go operational */
  if (goMsgRep.result != BCM_SUCCESS)
    return SOFT_ERROR(EHMIGOFW, hmiErr[EHMIGOFW]);

  return BCM_SUCCESS;
}
/* }}} */

#endif
