/*
 * Copyright (c) 2019, NXP. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <errno.h>

#include <lib/mmio.h>

#include <imx_rdc.h>

struct imx_uart {
	int index;
	unsigned int uart_base;
};

static const struct imx_uart imx8m_uart_info[] = {
	{	/* UART 1 */
		.index = RDC_PDAP_UART1,
		.uart_base = IMX_UART1_BASE,
	}, {	/* UART 2 */
		.index = RDC_PDAP_UART2,
		.uart_base = IMX_UART2_BASE,
	}, {	/* UART 3 */
		.index = RDC_PDAP_UART3,
		.uart_base = IMX_UART3_BASE,
	}, {	/* UART 4 */
		.index = RDC_PDAP_UART4,
		.uart_base = IMX_UART4_BASE,
	}
};

static int imx_rdc_uart_get_pdap_index(unsigned int uart_base)
{
	size_t i;

	for (i = 0; i < ARRAY_SIZE(imx8m_uart_info); i++) {
		if (imx8m_uart_info[i].uart_base == uart_base) {
			return imx8m_uart_info[i].index;
		}
	}

	return -ENODEV;
}

static void imx_rdc_console_access_enable(struct imx_rdc_cfg *rdc_cfg,
				   unsigned int console_base)
{
	struct imx_rdc_cfg *rdc;
	int console_pdap_index;

	console_pdap_index = imx_rdc_uart_get_pdap_index(console_base);
	if (console_pdap_index < 0) {
		return;
	}

	for (rdc = rdc_cfg; rdc->type != RDC_INVALID; rdc++) {
		if (rdc->type != RDC_PDAP || rdc->index != console_pdap_index) {
			continue;
		}

		if (rdc->index == console_pdap_index &&
		    rdc->setting.rdc_pdap == (D0R | D0W)) {
			return;
		}

		if (rdc->index == console_pdap_index) {
			rdc->setting.rdc_pdap = D0R | D0W;
		}
	}
}

void imx_rdc_init(struct imx_rdc_cfg *rdc_cfg, unsigned int console_base)
{
	struct imx_rdc_cfg *rdc = rdc_cfg;

	imx_rdc_console_access_enable(rdc, console_base);

	while (rdc->type != RDC_INVALID) {
		switch (rdc->type) {
		case RDC_MDA:
			/* MDA config */
			mmio_write_32(MDAn(rdc->index), rdc->setting.rdc_mda);
			break;
		case RDC_PDAP:
			/* peripheral access permission config */
			mmio_write_32(PDAPn(rdc->index), rdc->setting.rdc_pdap);
			break;
		case RDC_MEM_REGION:
			/* memory region access permission config */
			mmio_write_32(MRSAn(rdc->index), rdc->setting.rdc_mem_region[0]);
			mmio_write_32(MREAn(rdc->index), rdc->setting.rdc_mem_region[1]);
			mmio_write_32(MRCn(rdc->index), rdc->setting.rdc_mem_region[2]);
			break;
		default:
			break;
		}

		rdc++;
	}
}
