/* This source file is part of the ATMEL AVR32-SoftwareFramework-AT32AP7000-1.0.0 Release */

/*This file is prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief NEWLIB_ADDONS USART management file for AVR32.
 *
 * - Compiler:           GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices can be used.
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 ******************************************************************************/

/* Copyright (C) 2006-2008, Atmel Corporation All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include "nlao_io.h"
#include "nlao_usart.h"
#include "nlao_cpu.h"

static void *usart_base = (void *)0xFFE00C00;

#define usart_writel(value, reg) writel(value, usart_base + reg)
#define usart_writew(value, reg) writew(value, usart_base + reg)
#define usart_writeb(value, reg) writeb(value, usart_base + reg)
#define usart_readl(reg) readl(usart_base + reg)
#define usart_readw(reg) readw(usart_base + reg)
#define usart_readb(reg) readb(usart_base + reg)

void usart_setbrg(int baudrate, int cpu_clock)
{
  if (baudrate < 0 || cpu_clock < 0)
    return;

  struct usart3 *const usart = usart_base;
  unsigned int m, brg;

  /*
   *                 Master Clock
   * Baud Rate = --------------------, OVER = 16 or 8
   *              OVER * (CD + FP/8)
   *
   * We have to make sure the value is rounded properly...
   */
  unsigned int over = (cpu_clock >= 16 * baudrate) ? 16 : 8;
  unsigned int cd_fp = ((1 << US_BRGR_FP_SIZE) * cpu_clock + (over * baudrate) / 2) / (over * baudrate);
  unsigned int cd = cd_fp >> US_BRGR_FP_SIZE;
  unsigned int fp = cd_fp & ((1 << US_BRGR_FP_SIZE) - 1);

  if (cd < 1 || cd > (1 << US_BRGR_CD_SIZE) - 1)
    return;

  m = readl(&usart->us_mr);
  m = (m & ~US_MR_OVER) |
      ((over == 16) ? US_MR_OVER_X16 : US_MR_OVER_X8);
  writel(m, &usart->us_mr);

  brg = cd << US_BRGR_CD_OFFSET |
        fp << US_BRGR_FP_OFFSET;
  writel(brg, &usart->us_brgr);
}

void set_usart_base(void *base){
  usart_base = base;
}

int usart_init(int baudrate)
{
  struct usart3 *const usart = usart_base;

  writel(~0UL, &usart->us_idr);
  writel(US_CR_RSTRX | US_CR_RSTTX, &usart->us_cr);
  writel(US_MR_MODE_NORMAL | US_MR_CLKS_CLOCK |
         US_MR_CHRL_8BITS | US_MR_PAR_NONE |
         US_MR_NBSTOP_1BIT, &usart->us_mr);
  usart_setbrg(baudrate, get_cpu_hz());
  writel(US_CR_RXEN | US_CR_TXEN, &usart->us_cr);
  return 0;
}

void usart_putc(char c)
{
  struct usart3 *const usart = usart_base;

  while (!(readl(&usart->us_csr) & US_CSR_TXRDY)) ;
  writel(c, &usart->us_thr);
}

void usart_puts(const char *s)
{
  while (*s)
    usart_putc(*s++);
}

int usart_getc(void)
{
  struct usart3 *const usart = usart_base;
  while (!(readl(&usart->us_csr) & US_CSR_RXRDY)) ;
  return readl(&usart->us_rhr);
}

int usart_tstc(void)
{
  struct usart3 *const usart = usart_base;
  return (readl(&usart->us_csr) & US_CSR_RXRDY) != 0;
}
