/*
 * Copyright (c) 2022, Christopher Durand
 *
 * This file is part of the modm project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
// ----------------------------------------------------------------------------
#pragma once

#include <modm/architecture/interface/clock.hpp>
#include <modm/platform.hpp>
#include <modm/debug/logger.hpp>

#define MODM_BOARD_HAS_LOGGER

namespace Board
{
/// @ingroup modm_board_samv71_xplained_ultra
/// @{
using namespace modm::literals;
using namespace modm::platform;

struct SystemClock
{
	// 300 MHz system clock generated by PLLA from external 12 MHz crystal
	static constexpr uint32_t PllAMult = 25;
	static constexpr uint32_t Frequency = 300_MHz;
	static constexpr uint32_t Mck = Frequency / 2;  // 150 MHz max.
	static constexpr uint32_t Usart1 = Mck;
	static constexpr uint32_t Spi0 = Mck;
	static constexpr uint32_t Twihs0 = Mck;
	static constexpr uint32_t Dacc = Mck;

	static constexpr uint32_t Pck5 = 80_MHz;
	static constexpr uint32_t Mcan = Pck5;

//	static constexpr uint32_t Usb = 48_MHz;

	static bool inline
	enable()
	{
		ClockGen::setFlashLatency<Mck>();

		ClockGen::enableMainExternalCrystal<12_MHz>(std::chrono::microseconds{1000});
		ClockGen::selectMainClockSource(MainClockSource::External);

		ClockGen::enablePllA<PllAMult>();
		ClockGen::selectMasterClk<MasterClkSource::PLLA_CLK, MasterClkPrescaler::CLK_1, MasterClkDivider::Div2>();
		ClockGen::updateCoreFrequency<Frequency>();

		// Enable PMC bus-independent clock output PCK5 for MCAN
		// Manual (48.4.2): "It is recommended to use the CAN clock at frequencies of 20, 40 or 80 MHz.
		// To achieve these frequencies, PMC PCK5 must select the UPLLCK (480 MHz) as source clock and
		// divide by 24, 12, or 6. PCK5 allows the system bus and processor clock to be modified
		// without affecting the bit rate communication."
		ClockGen::enableUPll<UtmiRefClk::Xtal12>();
		ClockGen::disablePck(Pck::Pck5);
		ClockGen::configurePck(Pck::Pck5, PckSource::UPll, 6);
		ClockGen::enablePck(Pck::Pck5);

		return true;
	}
};

using Led0 = GpioInverted<GpioA23>;
using Led1 = GpioInverted<GpioC9>;
using ButtonSW0 = GpioInverted<GpioA9>;

using Leds = SoftwareGpioPort<Led1, Led0>;

struct Debug
{
	using Uart = Usart1;
	using UartTx = GpioB4;
	using UartRx = GpioA21;
};

using I2c = I2cMaster0;
using Sda = GpioA3;
using Scl = GpioA4;

using LoggerDevice = modm::IODeviceWrapper<Debug::Uart, modm::IOBuffer::BlockIfFull>;

namespace Can
{
/// @ingroup modm_board_samv71_xplained_ultra
/// @{
using Rx = GpioC12;
using Tx = GpioC14;
using Can = Mcan1;
/// @}
}

inline void
initialize()
{
	// Turn off the watchdog
	WDT->WDT_MR = WDT_MR_WDDIS_Msk;

	SystemClock::enable();
	SysTickTimer::initialize<SystemClock>();

	// Disable JTAG TDI function on debug UART TX pin
	MATRIX->CCFG_SYSIO |= CCFG_SYSIO_SYSIO4;

	Debug::Uart::initialize<SystemClock, 115200>();
	Debug::Uart::connect<Debug::UartTx::Tx, Debug::UartRx::Rx>();

	Leds::reset();
	Leds::setOutput();
	ButtonSW0::setInput(InputType::PullUp);

	Can::Can::connect<Can::Rx::Rx, Can::Tx::Tx>(InputType::PullUp);
}

/*
// TODO: usb
inline void initializeUsbFs()
{
	//SystemClock::enableUsb();
	//modm::platform::Usb::initialize<Board::SystemClock>();
}
*/
/// @}

} // namespace Board

