#!/usr/bin/perl -w # SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause # Copyright (C) 2019--2020 Intel Corporation use Getopt::Long qw(:config no_ignore_case); use File::Basename; my $ccsregs = "ccs-regs.asc"; my $header; my $regarray; my $limitc; my $limith; my $kernel; my $help; GetOptions("ccsregs|c=s" => \$ccsregs, "header|e=s" => \$header, "regarray|r=s" => \$regarray, "limitc|l=s" => \$limitc, "limith|L=s" => \$limith, "kernel|k" => \$kernel, "help|h" => \$help) or die "can't parse options"; $help = 1 if ! defined $header || ! defined $limitc || ! defined $limith; if (defined $help) { print <<EOH $0 - Create CCS register definitions for C usage: $0 -c ccs-regs.asc -e header -r regarray -l limit-c -L limit-header [-k] -c ccs register file -e header file name -r register description array file name -l limit and capability array file name -L limit and capability header file name -k generate files for kernel space consumption EOH ; exit 0; } my $lh_hdr = ! defined $kernel ? '#include "ccs-os.h"' . "\n" : "#include <linux/bits.h>\n#include <linux/types.h>\n"; my $uint32_t = ! defined $kernel ? 'uint32_t' : 'u32'; my $uint16_t = ! defined $kernel ? 'uint16_t' : 'u16'; open(my $R, "< $ccsregs") or die "can't open $ccsregs"; open(my $H, "> $header") or die "can't open $header"; my $A; if (defined $regarray) { open($A, "> $regarray") or die "can't open $regarray"; } open(my $LC, "> $limitc") or die "can't open $limitc"; open(my $LH, "> $limith") or die "can't open $limith"; my %this; sub is_limit_reg($) { my $addr = hex $_[0]; return 0 if $addr < 0x40; # weed out status registers return 0 if $addr >= 0x100 && $addr < 0xfff; # weed out configuration registers return 1; } my $uc_header = basename uc $header; $uc_header =~ s/[^A-Z0-9]/_/g; my $copyright = "/* Copyright (C) 2019--2020 Intel Corporation */\n"; my $license = "SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause"; my $note = "/*\n * Generated by $0;\n * do not modify.\n */\n"; for my $fh ($A, $LC) { print $fh "// $license\n$copyright$note\n" if defined $fh; } for my $fh ($H, $LH) { print $fh "/* $license */\n$copyright$note\n"; } sub bit_def($) { my $bit = shift @_; return "BIT($bit)" if defined $kernel; return "(1U << $bit)" if $bit =~ /^[a-zA-Z0-9_]+$/; return "(1U << ($bit))"; } print $H <<EOF #ifndef __${uc_header}__ #define __${uc_header}__ EOF ; print $H "#include <linux/bits.h>\n\n" if defined $kernel; print $H <<EOF #define CCS_FL_BASE 16 EOF ; print $H "#define CCS_FL_16BIT " . bit_def("CCS_FL_BASE") . "\n"; print $H "#define CCS_FL_32BIT " . bit_def("CCS_FL_BASE + 1") . "\n"; print $H "#define CCS_FL_FLOAT_IREAL " . bit_def("CCS_FL_BASE + 2") . "\n"; print $H "#define CCS_FL_IREAL " . bit_def("CCS_FL_BASE + 3") . "\n"; print $H <<EOF #define CCS_R_ADDR(r) ((r) & 0xffff) EOF ; print $A <<EOF #include <stdint.h> #include <stdio.h> #include "ccs-extra.h" #include "ccs-regs.h" EOF if defined $A; my $uc_limith = basename uc $limith; $uc_limith =~ s/[^A-Z0-9]/_/g; print $LH <<EOF #ifndef __${uc_limith}__ #define __${uc_limith}__ $lh_hdr struct ccs_limit { $uint32_t reg; $uint16_t size; $uint16_t flags; const char *name; }; EOF ; print $LH "#define CCS_L_FL_SAME_REG " . bit_def(0) . "\n\n"; print $LH <<EOF extern const struct ccs_limit ccs_limits[]; EOF ; print $LC <<EOF #include "ccs-limits.h" #include "ccs-regs.h" const struct ccs_limit ccs_limits[] = { EOF ; my $limitcount = 0; my $argdescs; my $reglist = "const struct ccs_reg_desc ccs_reg_desc[] = {\n"; sub name_split($$) { my ($name, $addr) = @_; my $args; $name =~ /([^\(]+?)(\(.*)/; ($name, $args) = ($1, $2); $args = [split /,\s*/, $args]; foreach my $t (@$args) { $t =~ s/[\(\)]//g; $t =~ s/\//\\\//g; } return ($name, $addr, $args); } sub tabconv($) { $_ = shift; my @l = split "\n", $_; map { s/ {8,8}/\t/g; s/\t\K +//; } @l; return (join "\n", @l) . "\n"; } sub elem_size(@) { my @flags = @_; return 2 if grep /^16$/, @flags; return 4 if grep /^32$/, @flags; return 1; } sub arr_size($) { my $this = $_[0]; my $size = $this->{elsize}; my $h = $this->{argparams}; foreach my $arg (@{$this->{args}}) { my $apref = $h->{$arg}; $size *= $apref->{max} - $apref->{min} + 1; } return $size; } sub print_args($$$) { my ($this, $postfix, $is_same_reg) = @_; my ($args, $argparams, $name) = ($this->{args}, $this->{argparams}, $this->{name}); my $varname = "ccs_reg_arg_" . (lc $name) . $postfix; my @mins; my @sorted_args = @{$this->{sorted_args}}; my $lim_arg; my $size = arr_size($this); $argdescs .= "static const struct ccs_reg_arg " . $varname . "[] = {\n"; foreach my $sorted_arg (@sorted_args) { push @mins, $argparams->{$sorted_arg}->{min}; } foreach my $sorted_arg (@sorted_args) { my $h = $argparams->{$sorted_arg}; $argdescs .= "\t{ \"$sorted_arg\", $h->{min}, $h->{max}, $h->{elsize} },\n"; $lim_arg .= defined $lim_arg ? ", $h->{min}" : "$h->{min}"; } $argdescs .= "};\n\n"; $reglist .= "\t{ CCS_R_" . (uc $name) . "(" . (join ",", (@mins)) . "), $size, sizeof($varname) / sizeof(*$varname)," . " \"" . (lc $name) . "\", $varname },\n"; print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . "($lim_arg), " . $size . ", " . ($is_same_reg ? "CCS_L_FL_SAME_REG" : "0") . ", \"$name" . (defined $this->{discontig} ? " $lim_arg" : "") . "\" },\n" if is_limit_reg $this->{base_addr}; } my $hdr_data; while (<$R>) { chop; s/^\s*//; next if /^[#;]/ || /^$/; if (s/^-\s*//) { if (s/^b\s*//) { my ($bit, $addr) = split /\t+/; $bit = uc $bit; $hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) ."_$bit", bit_def($addr) . "\n"; } elsif (s/^f\s*//) { s/[,\.-]/_/g; my @a = split /\s+/; my ($msb, $lsb, $this_field) = reverse @a; @a = ( { "name" => "SHIFT", "addr" => $lsb, "fmt" => "%uU", }, { "name" => "MASK", "addr" => (1 << ($msb + 1)) - 1 - ((1 << $lsb) - 1), "fmt" => "0x%" . join(".", ($this{"elsize"} >> 2) x 2) . "x" } ); $this{"field"} = $this_field; foreach my $ar (@a) { #print $ar->{fmt}."\n"; $hdr_data .= sprintf "#define %-62s " . $ar->{"fmt"} . "\n", "CCS_" . (uc $this{"name"}) . (defined $this_field ? "_" . uc $this_field : "") . "_" . $ar->{"name"}, $ar->{"addr"} . "\n"; } } elsif (s/^e\s*//) { s/[,\.-]/_/g; my ($enum, $addr) = split /\s+/; $enum = uc $enum; $hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) . (defined $this{"field"} ? "_" . uc $this{"field"} : "") ."_$enum", $addr . ($addr =~ /0x/i ? "" : "U") . "\n"; } elsif (s/^l\s*//) { my ($arg, $min, $max, $elsize, @discontig) = split /\s+/; my $size; foreach my $num ($min, $max) { $num = hex $num if $num =~ /0x/i; } $hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MIN_$arg"), $min . ($min =~ /0x/i ? "" : "U") . "\n"; $hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MAX_$arg"), $max . ($max =~ /0x/i ? "" : "U") . "\n"; my $h = $this{argparams}; $h->{$arg} = { "min" => $min, "max" => $max, "elsize" => $elsize =~ /^0x/ ? hex $elsize : $elsize, "discontig" => \@discontig }; $this{discontig} = $arg if @discontig; next if $#{$this{args}} + 1 != scalar keys %{$this{argparams}}; my $reg_formula = "($this{addr}"; my $lim_formula; foreach my $arg (@{$this{args}}) { my $d = $h->{$arg}->{discontig}; my $times = $h->{$arg}->{elsize} != 1 ? " * " . $h->{$arg}->{elsize} : ""; if (@$d) { my ($lim, $offset) = split /,/, $d->[0]; $reg_formula .= " + (($arg) < $lim ? ($arg)$times : $offset + (($arg) - $lim)$times)"; } else { $reg_formula .= " + ($arg)$times"; } $lim_formula .= (defined $lim_formula ? " + " : "") . "($arg)$times"; } $reg_formula .= ")\n"; $lim_formula =~ s/^\(([a-z0-9]+)\)$/$1/i; print $H tabconv sprintf("#define %-62s %s", "CCS_R_" . (uc $this{name}) . $this{arglist}, $reg_formula); print $H tabconv $hdr_data; undef $hdr_data; # Sort arguments in descending order by size @{$this{sorted_args}} = sort { $h->{$a}->{elsize} <= $h->{$b}->{elsize} } @{$this{args}}; if (defined $this{discontig}) { my $da = $this{argparams}->{$this{discontig}}; my ($first_discontig) = split /,/, $da->{discontig}->[0]; my $max = $da->{max}; $da->{max} = $first_discontig - 1; print_args(\%this, "", 0); $da->{min} = $da->{max} + 1; $da->{max} = $max; print_args(\%this, $first_discontig, 1); } else { print_args(\%this, "", 0); } next unless is_limit_reg $this{base_addr}; print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_" . (uc $this{name}) . "_OFFSET(" . (join ", ", @{$this{args}}) . ")", "($lim_formula)"; } if (! @{$this{args}}) { print $H tabconv($hdr_data); undef $hdr_data; } next; } my ($name, $addr, @flags) = split /\t+/, $_; my $args = []; my $sp; ($name, $addr, $args) = name_split($name, $addr) if /\(.*\)/; $name =~ s/[,\.-]/_/g; my $flagstring = ""; my $size = elem_size(@flags); $flagstring .= "| CCS_FL_16BIT " if $size eq "2"; $flagstring .= "| CCS_FL_32BIT " if $size eq "4"; $flagstring .= "| CCS_FL_FLOAT_IREAL " if grep /^float_ireal$/, @flags; $flagstring .= "| CCS_FL_IREAL " if grep /^ireal$/, @flags; $flagstring =~ s/^\| //; $flagstring =~ s/ $//; $flagstring = "($flagstring)" if $flagstring =~ /\|/; my $base_addr = $addr; $addr = "($addr | $flagstring)" if $flagstring ne ""; my $arglist = @$args ? "(" . (join ", ", @$args) . ")" : ""; $hdr_data .= sprintf "#define %-62s %s\n", "CCS_R_" . (uc $name), $addr if !@$args; $name =~ s/\(.*//; %this = ( name => $name, addr => $addr, base_addr => $base_addr, argparams => {}, args => $args, arglist => $arglist, elsize => $size, ); if (!@$args) { $reglist .= "\t{ CCS_R_" . (uc $name) . ", 1, 0, \"" . (lc $name) . "\", NULL },\n"; print $H tabconv $hdr_data; undef $hdr_data; print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . ", " . $this{elsize} . ", 0, \"$name\" },\n" if is_limit_reg $this{base_addr}; } print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_" . (uc $this{name}), $limitcount++ if is_limit_reg $this{base_addr}; } if (defined $A) { print $A $argdescs, $reglist; print $A "\t{ 0 }\n"; print $A "};\n"; } print $H "\n#endif /* __${uc_header}__ */\n"; print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_LAST", $limitcount; print $LH "\n#endif /* __${uc_limith}__ */\n"; print $LC "\t{ 0 } /* Guardian */\n"; print $LC "};\n"; close($R); close($H); close($A) if defined $A; close($LC); close($LH);