#!/usr/bin/env awk
# SPDX-License-Identifier: GPL-2.0
# gen-sysreg.awk: arm64 sysreg header generator
#
# Usage: awk -f gen-sysreg.awk sysregs.txt

# Log an error and terminate
function fatal(msg) {
	print "Error at " NR ": " msg > "/dev/stderr"
	exit 1
}

# Sanity check that the start or end of a block makes sense at this point in
# the file. If not, produce an error and terminate.
#
# @this - the $Block or $EndBlock
# @prev - the only valid block to already be in (value of @block)
# @new - the new value of @block
function change_block(this, prev, new) {
	if (block != prev)
		fatal("unexpected " this " (inside " block ")")

	block = new
}

# Sanity check the number of records for a field makes sense. If not, produce
# an error and terminate.
function expect_fields(nf) {
	if (NF != nf)
		fatal(NF " fields found where " nf " expected")
}

# Print a CPP macro definition, padded with spaces so that the macro bodies
# line up in a column
function define(name, val) {
	printf "%-48s%s\n", "#define " name, val
}

# Print standard BITMASK/SHIFT/WIDTH CPP definitions for a field
function define_field(reg, field, msb, lsb) {
	define(reg "_" field, "GENMASK(" msb ", " lsb ")")
	define(reg "_" field "_MASK", "GENMASK(" msb ", " lsb ")")
	define(reg "_" field "_SHIFT", lsb)
	define(reg "_" field "_WIDTH", msb - lsb + 1)
}

# Parse a "<msb>[:<lsb>]" string into the global variables @msb and @lsb
function parse_bitdef(reg, field, bitdef, _bits)
{
	if (bitdef ~ /^[0-9]+$/) {
		msb = bitdef
		lsb = bitdef
	} else if (split(bitdef, _bits, ":") == 2) {
		msb = _bits[1]
		lsb = _bits[2]
	} else {
		fatal("invalid bit-range definition '" bitdef "'")
	}


	if (msb != next_bit)
		fatal(reg "." field " starts at " msb " not " next_bit)
	if (63 < msb || msb < 0)
		fatal(reg "." field " invalid high bit in '" bitdef "'")
	if (63 < lsb || lsb < 0)
		fatal(reg "." field " invalid low bit in '" bitdef "'")
	if (msb < lsb)
		fatal(reg "." field " invalid bit-range '" bitdef "'")
	if (low > high)
		fatal(reg "." field " has invalid range " high "-" low)

	next_bit = lsb - 1
}

BEGIN {
	print "#ifndef __ASM_SYSREG_DEFS_H"
	print "#define __ASM_SYSREG_DEFS_H"
	print ""
	print "/* Generated file - do not edit */"
	print ""

	block = "None"
}

END {
	print "#endif /* __ASM_SYSREG_DEFS_H */"
}

# skip blank lines and comment lines
/^$/ { next }
/^[\t ]*#/ { next }

/^SysregFields/ {
	change_block("SysregFields", "None", "SysregFields")
	expect_fields(2)

	reg = $2

	res0 = "UL(0)"
	res1 = "UL(0)"

	next_bit = 63

	next
}

/^EndSysregFields/ {
	if (next_bit > 0)
		fatal("Unspecified bits in " reg)

	change_block("EndSysregFields", "SysregFields", "None")

	define(reg "_RES0", "(" res0 ")")
	define(reg "_RES1", "(" res1 ")")
	print ""

	reg = null
	res0 = null
	res1 = null

	next
}

/^Sysreg/ {
	change_block("Sysreg", "None", "Sysreg")
	expect_fields(7)

	reg = $2
	op0 = $3
	op1 = $4
	crn = $5
	crm = $6
	op2 = $7

	res0 = "UL(0)"
	res1 = "UL(0)"

	define("REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
	define("SYS_" reg, "sys_reg(" op0 ", " op1 ", " crn ", " crm ", " op2 ")")

	define("SYS_" reg "_Op0", op0)
	define("SYS_" reg "_Op1", op1)
	define("SYS_" reg "_CRn", crn)
	define("SYS_" reg "_CRm", crm)
	define("SYS_" reg "_Op2", op2)

	print ""

	next_bit = 63

	next
}

/^EndSysreg/ {
	if (next_bit > 0)
		fatal("Unspecified bits in " reg)

	change_block("EndSysreg", "Sysreg", "None")

	if (res0 != null)
		define(reg "_RES0", "(" res0 ")")
	if (res1 != null)
		define(reg "_RES1", "(" res1 ")")
	if (res0 != null || res1 != null)
		print ""

	reg = null
	op0 = null
	op1 = null
	crn = null
	crm = null
	op2 = null
	res0 = null
	res1 = null

	next
}

# Currently this is effectivey a comment, in future we may want to emit
# defines for the fields.
/^Fields/ && (block == "Sysreg") {
	expect_fields(2)

	if (next_bit != 63)
		fatal("Some fields already defined for " reg)

	print "/* For " reg " fields see " $2 " */"
	print ""

        next_bit = 0
	res0 = null
	res1 = null

	next
}


/^Res0/ && (block == "Sysreg" || block == "SysregFields") {
	expect_fields(2)
	parse_bitdef(reg, "RES0", $2)
	field = "RES0_" msb "_" lsb

	res0 = res0 " | GENMASK_ULL(" msb ", " lsb ")"

	next
}

/^Res1/ && (block == "Sysreg" || block == "SysregFields") {
	expect_fields(2)
	parse_bitdef(reg, "RES1", $2)
	field = "RES1_" msb "_" lsb

	res1 = res1 " | GENMASK_ULL(" msb ", " lsb ")"

	next
}

/^Field/ && (block == "Sysreg" || block == "SysregFields") {
	expect_fields(3)
	field = $3
	parse_bitdef(reg, field, $2)

	define_field(reg, field, msb, lsb)
	print ""

	next
}

/^Raz/ && (block == "Sysreg" || block == "SysregFields") {
	expect_fields(2)
	parse_bitdef(reg, field, $2)

	next
}

/^Enum/ {
	change_block("Enum", "Sysreg", "Enum")
	expect_fields(3)
	field = $3
	parse_bitdef(reg, field, $2)

	define_field(reg, field, msb, lsb)

	next
}

/^EndEnum/ {
	change_block("EndEnum", "Enum", "Sysreg")
	field = null
	msb = null
	lsb = null
	print ""
	next
}

/0b[01]+/ && block == "Enum" {
	expect_fields(2)
	val = $1
	name = $2

	define(reg "_" field "_" name, "UL(" val ")")
	next
}

# Any lines not handled by previous rules are unexpected
{
	fatal("unhandled statement")
}
