/*
 * Command for accessing Ethernet efuse.
 *
 * Copyright (C) Realsil Corporation
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */
#include <common.h>
#include <asm/io.h>
#include "bspchip.h"

#define EFUSE_DEFAUTL_V	0x2e
#define EFUSE_READ_DONE	0x1
#define EFUSE_DEFAULT	0x12e00
#define readl(addr) (*(volatile unsigned int *)(addr))
#define writel(b, addr) (*(volatile unsigned int *)(addr)) = (b)

#define DW_BIT_SET0(__datum, __bfws)                          \
	((__datum) = ((uint32_t) (__datum) & ~DW_BIT_MASK(__bfws)) &    \
	~(DW_BIT_MASK(__bfws)))

void efuse_config(u32 st_cnt)
{
	u32 tmp;

	tmp = readl(SYS_EFUSE_CFG);
	DW_BIT_SET(tmp, EFUSE_POW33);
	writel(tmp, SYS_EFUSE_CFG);

	tmp = st_cnt & 0xff;
	writel(tmp, SYS_EFUSE_CNT);
}

unsigned char efuse_read_byte(unsigned int addr)
{
	u32 tmp;
	u32 waddr, baddr, val;
	u8 valb;

	waddr = addr / 4;
	baddr = addr % 4;

	efuse_config(120);

	tmp = readl(SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_PD);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_PS);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_LOAD);
	DW_BIT_SET(tmp, EFUSE_PGENB);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_CSB);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BITS_SET_VAL(tmp, 0, waddr, 8);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_STROBE);
	writel(tmp, SYS_EFUSE_CFG);

	writel((readl(SYS_EFUSE_RDATA)), (SYS_EFUSE_OUT0 + waddr * 4));

	DW_BIT_SET0(tmp, EFUSE_STROBE);
	writel(tmp, SYS_EFUSE_CFG);

	val = readl(SYS_EFUSE_OUT0 + waddr * 4);

	writel(EFUSE_READ_DONE, SYS_EFUSE_CTL);

	udelay(10);
	writel(EFUSE_DEFAUTL_V, SYS_EFUSE_CFG);

	valb = ((u8 *)&val)[baddr];

	return valb;
}

void efuse_write(u32 dst, u32 *data, int len)
{
	u32 tmp;
	u32 addr;
	u32 i, j, k;

	len /= 4;
	dst /= 4;

	efuse_config(120);

	tmp = readl(SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, PROGRAM_POW);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_PD);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_PS);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_LOAD);
	DW_BIT_SET0(tmp, EFUSE_PGENB);
	DW_BIT_SET0(tmp, EFUSE_CSB);
	writel(tmp, SYS_EFUSE_CFG);

	for (i = 0; i < len; i++) {
		k = 1;
		for (j = 0; j < 32; j++) {
			if (data[i] & k) {
				addr = (j << 3) | (dst + i);
				tmp = readl(SYS_EFUSE_CFG);
				DW_BITS_SET_VAL(tmp, 0, addr, 8);
				writel(tmp, SYS_EFUSE_CFG);

				DW_BIT_SET(tmp, EFUSE_STROBE);
				writel(tmp, SYS_EFUSE_CFG);
				udelay(4);
			}
			k = k << 1;
		}
	}

	tmp = readl(SYS_EFUSE_CFG);
	DW_BIT_SET(tmp, EFUSE_LOAD);
	DW_BIT_SET(tmp, EFUSE_PGENB);
	DW_BIT_SET(tmp, EFUSE_CSB);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_PS);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_PD);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, PROGRAM_POW);
	writel(tmp, SYS_EFUSE_CFG);

	udelay(10);
	writel(EFUSE_DEFAUTL_V, SYS_EFUSE_CFG);
}

void efuse_before_load_aes(void)
{
	efuse_config(120);
	writel(EFUSE_DEFAULT, SYS_EFUSE_CFG);
}

void efuse_load_aes(void)
{
	u32 tmp;
	u32 i;

	efuse_config(120);

	tmp = readl(SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_PD);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_CSB);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET0(tmp, EFUSE_PS);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_LOAD);
	DW_BIT_SET(tmp, EFUSE_PGENB);
	writel(tmp, SYS_EFUSE_CFG);

	for (i = 0; i < 4; i++) {
		DW_BITS_SET_VAL(tmp, 0, i, 8);
		writel(tmp, SYS_EFUSE_CFG);

		DW_BIT_SET(tmp, EFUSE_STROBE);
		writel(tmp, SYS_EFUSE_CFG);

		writel((readl(SYS_EFUSE_RDATA)), (SYS_EFUSE_OUT0 + i * 4));

		DW_BIT_SET0(tmp, EFUSE_STROBE);
		writel(tmp, SYS_EFUSE_CFG);

		DW_BIT_SET(tmp, LOAD_AES_KEY);
		writel(tmp, SYS_EFUSE_CFG);

		DW_BIT_SET0(tmp, LOAD_AES_KEY);
		writel(tmp, SYS_EFUSE_CFG);
	}

	tmp = readl(SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_PD);
	writel(tmp, SYS_EFUSE_CFG);

	DW_BIT_SET(tmp, EFUSE_CSB);
	writel(tmp, SYS_EFUSE_CFG);

	tmp = 1;
	writel(tmp, SYS_EFUSE_CTL);

	udelay(10);
	writel(EFUSE_DEFAUTL_V, SYS_EFUSE_CFG);
}

