#!/usr/bin/perl -w
#
# Copyright © 2015 Collabora, Ltd.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# Author: Daniel Stone <daniels@collabora.com>
#
#
# This is a script to pull in DeviceTree definitions for Tegra pinmuxes from
# a downstream kernel, and attempt to reconcile it with current upstream. It
# is a pretty horrendous kludge.
#
# When run against the downstream DT and various ancillary files (see below),
# it will output a DT section that can be easily diffed (recommend -dw) against
# the output from board-to-kernel-dt.py.
#
# Apologies to anyone who has to read this.

# The pinmux section from the downstream DT, with leading tabs removed.
$dts_in = "tegra124-nyan.txt";
open(dts_in) or die("could not open $dts_in");

# Ordered list of names for output.
$munged_in = "munged";
open(munged_in) or die("could not open $munged_in");

# Name -> register mapping from pinctrl-tegra124.c.
$cros_pins_in = "names-chromeos.txt";
open(cros_pins_in) or die("could not open $cros_pins_in");
# Ditto, but upstream.
$upstream_pins_in = "names-upstream.txt";
open(upstream_pins_in) or die("could not open $upstream_pins_in");

%cros_reg_name_map = ();
%upstr_name_reg_map = ();
%out_pins = ();
$state = "outer";
foreach $line (<dts_in>) {
	# throw away the original name
	if ($state eq "outer") {
		$state = "need_pins";
		@defs = ();
		@pin_names = ();
		$num_pins = 0;
		$have_initial_pins = 0;
	}
	elsif ($state eq "need_pins") {
		if ($have_initial_pins == 0) {
			$have_initial_pins = 1;
			$line =~ m/^\s*nvidia,pins = (.*)\n$/;
			@my_pins = split("\"", $1);
		}
		else {
			$line =~ m/\s*(.*)\n$/;
			@my_pins = split("\"", $1);
		};
		$len = @my_pins - 1;
		if ($my_pins[$len] eq ";") {
			$state = "in_pin";
			$len--;
		}
		push(@pin_names, @my_pins);
		$num_pins += $len;
	}
	elsif ($state eq "in_pin") {
		if ($line eq "};\n") {
			foreach $pin (@pin_names) {
				if ($pin eq "" || $pin eq "," || $pin eq ";") {
					;
				}
				else {
					$out_pins{$pin} = "$pin {\n\tnvidia,pins = \"$pin\";\n@defs};\n";
				}
			}
			$state = "outer";
		}
		else {
			if (not $line =~ /\s*nvidia,lock/) {
				push(@defs, $line);					
			}
		}
	}
	else {
		die("impossible state $state");
	}
}

close(dts_in);

foreach $line (<cros_pins_in>) {
	chomp $line;
	@res = split(",", $line);
	$res[0] =~ s/^\s*PINGROUP\(//;
	$res[6] =~ s/^\s*//;
	$cros_reg_name_map{$res[6]} = $res[0];
}
close(cros_pins_in);

foreach $line (<upstream_pins_in>) {
	chomp $line;
	@res = split(",", $line);
	$res[0] =~ s/^\s*PINGROUP\(//;
	$res[5] =~ s/^\s*//;
	$upstr_name_reg_map{$res[0]} = $res[5];
}
close(upstream_pins_in);

foreach $line (<munged_in>) {
	chomp $line;
	if (not exists $upstr_name_reg_map{$line}) {
		print STDERR "COULDN'T FIND '$line' UPSTREAM\n";
	}
	elsif (not exists $cros_reg_name_map{$upstr_name_reg_map{$line}}) {
		print STDERR "COULDN'T FIND CROS MAPPING for '$line' ($upstr_name_reg_map{$line})\n";
	}
	elsif (not exists $out_pins{$cros_reg_name_map{$upstr_name_reg_map{$line}}}) {
		print STDERR "NO DEFINED MAPPING FOR $line (reg $upstr_name_reg_map{$line}, cros name $cros_reg_name_map{$upstr_name_reg_map{$line}})\n";
	}
	else {
	    print $out_pins{$cros_reg_name_map{$upstr_name_reg_map{$line}}};
	    delete $out_pins{$cros_reg_name_map{$upstr_name_reg_map{$line}}};
	}
}

close(munged_in);

foreach $key (keys %out_pins) {
	print STDERR "UNUSED PIN $key\n";
}
