/*
 * Copyright © 2019 Keith Packard <keithp@keithp.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

#ifndef _AO_ARCH_H_
#define _AO_ARCH_H_

#include <stdio.h>
#include <samd21.h>

/*
 * Samd21 definitions and code fragments for AltOS
 */

#define AO_PORT_TYPE	uint32_t

#define AO_LED_TYPE	AO_PORT_TYPE

#define ao_arch_naked_declare	__attribute__((naked))
#define ao_arch_naked_define

#define ao_arch_reboot() \
	(samd21_scb.aircr = ((SAMD21_SCB_AIRCR_VECTKEY_KEY << SAMD21_SCB_AIRCR_VECTKEY) | \
			  (1 << SAMD21_SCB_AIRCR_SYSRESETREQ)))

#define ao_arch_nop()		asm("nop")
#define ao_arch_interrupt(n)	/* nothing */
#define ao_arch_block_interrupts()	asm("cpsid i")
#define ao_arch_release_interrupts()	asm("cpsie i")

/* ao_romconfig.c */
#define AO_ROMCONFIG_SYMBOL __attribute__((section(".init.1"))) const
#define AO_USBCONFIG_SYMBOL __attribute__((section(".init.2"))) const

/*
 * ao_timer.c
 *
 * For the samd21, we want to use the DFLL48 clock
 */

/* GCLK 0 is always the system clock source */

#define AO_GCLK_SYSCLK	0

/* If there's a 32kHz xtal, use that for the 32kHz oscillator */
#ifdef AO_XOSC32K
# ifndef AO_GCLK_XOSC32K
#  define AO_GCLK_XOSC32K	1
# endif
#endif

/* If there's a high-freq xtal, use that */
#ifdef AO_XOSC
# ifndef AO_GCLK_XOSC
#  define AO_GCLK_XOSC	1
# endif

# ifndef AO_XOSC_GCLK_DIV
#  define AO_XOSC_GCLK_DIV 1
# endif

# define AO_FDPLL96M	((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV)

/* By default, use the xosc for the system, but allow the dfll48m to
 * drive USB if desired.
 */

# ifndef AO_GCLK_FDPLL96M
#  define AO_SYSCLK		AO_FDPLL96M
#  define AO_GCLK_FDPLL96M	AO_GCLK_SYSCLK
#  define AO_GCLK_DFLL48M	2
# endif

#endif	/* AO_XOSC */

#if AO_DFLL48M
# ifndef AO_GCLK_DFLL48M
#  define AO_SYSCLK	AO_DFLL48M
#  define AO_GCLK_DFLL48M AO_GCLK_SYSCLK
# endif
#endif

#ifndef AO_GCLK_USB
# if AO_DFLL48M
#  define AO_GCLK_USB AO_GCLK_DFLL48M
# else
#  define AO_GCLK_USB AO_GCLK_SYSCLK
# endif
#endif

/*
 * ao_timer.c
 *
 * For the samd21, we want to use the DFLL48 clock
 */

/* GCLK 0 is always the system clock source */

#define AO_GCLK_SYSCLK	0

/* If there's a 32kHz xtal, use that for the 32kHz oscillator */
#ifdef AO_XOSC32K
# ifndef AO_GCLK_XOSC32K
#  define AO_GCLK_XOSC32K	1
# endif
#endif

/* If there's a high-freq xtal, use that */
#ifdef AO_XOSC
# ifndef AO_GCLK_XOSC
#  define AO_GCLK_XOSC	1
# endif

# ifndef AO_XOSC_GCLK_DIV
#  define AO_XOSC_GCLK_DIV 1
# endif

# define AO_FDPLL96M	((AO_XOSC_FREQ / AO_XOSC_DIV * AO_XOSC_MUL) / AO_XOSC_GCLK_DIV)

/* By default, use the xosc for the system, but allow the dfll48m to
 * drive USB if desired.
 */

# ifndef AO_GCLK_FDPLL96M
#  define AO_SYSCLK		AO_FDPLL96M
#  define AO_GCLK_FDPLL96M	AO_GCLK_SYSCLK
#  define AO_GCLK_DFLL48M	2
# endif

#endif	/* AO_XOSC */

#if AO_DFLL48M
# ifndef AO_GCLK_DFLL48M
#  define AO_SYSCLK	AO_DFLL48M
#  define AO_GCLK_DFLL48M AO_GCLK_SYSCLK
# endif
#endif

#ifndef AO_GCLK_USB
# if AO_DFLL48M
#  define AO_GCLK_USB AO_GCLK_DFLL48M
# else
#  define AO_GCLK_USB AO_GCLK_SYSCLK
# endif
#endif

#define AO_HCLK		(AO_SYSCLK / AO_AHB_PRESCALER)

#define AO_HCLK		(AO_SYSCLK / AO_AHB_PRESCALER)
#define AO_PCLK		(AO_HCLK / AO_APB_PRESCALER)
#define AO_SYSTICK	(AO_HCLK)
#define AO_PANIC_DELAY_SCALE  (AO_SYSCLK / 12000000)

#define AO_SAMD21_NVIC_HIGH_PRIORITY	(0 << 6)
#define AO_SAMD21_NVIC_CLOCK_PRIORITY	(1 << 6)
#define AO_SAMD21_NVIC_MED_PRIORITY	(2 << 6)
#define AO_SAMD21_NVIC_LOW_PRIORITY	(3 << 6)

/* ADC maximum reported value */
#define AO_ADC_MAX			4095

#define AO_SAMD21_NVIC_HIGH_PRIORITY	(0 << 6)
#define AO_SAMD21_NVIC_CLOCK_PRIORITY	(1 << 6)
#define AO_SAMD21_NVIC_MED_PRIORITY	(2 << 6)
#define AO_SAMD21_NVIC_LOW_PRIORITY	(3 << 6)

/* ADC maximum reported value */
#define AO_ADC_MAX			4095

/* This has to be 65536 so that TCC and TC match; TC isn't configurable */
#define AO_TCC_PERIOD		65536
#define SNEK_PWM_MAX		(AO_TCC_PERIOD-1)

#define AO_TICK_TYPE		uint32_t
#define AO_TICK_SIGNED		int32_t

bool
ao_usb_waiting(void);

#define AO_CMD_LEN		128

#ifndef AO_STACK_SIZE
#define AO_STACK_SIZE		512
#endif

#ifndef HAS_BOOT_LOADER
#define HAS_BOOT_LOADER			1
#endif

#if HAS_BOOT_LOADER
#define AO_BOOT_APPLICATION_BASE	((uint32_t *) 0x00001000)
#ifndef AO_BOOT_APPLICATION_BOUND
#define AO_BOOT_APPLICATION_BOUND	((uint32_t *) (0x00000000 + samd21_flash_size()))
#endif
#define AO_BOOT_LOADER_BASE		((uint32_t *) 0x00000000)
#endif

#endif /* _AO_ARCH_H_ */
