Add I2C nodes to device tree generation.
Also ensured global variable attributes were filled
in, and renamed STANDALONE_PROPERTY to ZERO_LENGTH property.
Change-Id: Ie7435349b8e07b4644da02403bcfbc5d64baa48c
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/gen_devtree.pl b/gen_devtree.pl
index e42eab0..50c9a77 100755
--- a/gen_devtree.pl
+++ b/gen_devtree.pl
@@ -9,8 +9,14 @@
use Getopt::Long;
use constant VERSION => "/dts-v1/;";
-use constant STANDALONE_PROPERTY => "standalone_property";
-use constant DTSI_INCLUDE => "DTSI_INCLUDE";
+use constant ZERO_LENGTH_PROPERTY => "zero_length_property";
+
+#For converting MRW I2C Bus numbers to Linux relative ones.
+my %I2C_BUS_ADJUST = (
+ AST2400 => -1,
+ AST2500 => -1
+);
+
my $serverwizFile;
my $outputFile;
@@ -28,14 +34,9 @@
my $g_targetObj = Targets->new;
$g_targetObj->loadXML($serverwizFile);
-my $g_bmc = getBMCTarget();
-if (length($g_bmc) == 0) {
- die "Unable to find a BMC in this system\n";
-}
+my ($g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName);
+setGlobalAttributes();
-my $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
-my $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
-my $g_systemName = $g_targetObj->getSystemName();
open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
@@ -56,6 +57,7 @@
printRootNodeEnd($f, 0);
+printNodes($f, 0, getI2CNodes());
printNodes($f, 0, getMacNodes());
printNodes($f, 0, getUARTNodes());
printNodes($f, 0, getVuartNodes());
@@ -64,6 +66,32 @@
exit 0;
+#Finds the values for these globals:
+# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
+sub setGlobalAttributes()
+{
+ $g_bmc = getBMCTarget();
+ if (length($g_bmc) == 0) {
+ die "Unable to find a BMC in this system\n";
+ }
+
+ if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
+ die "The MODEL attribute on $g_bmc is missing or empty.\n";
+ }
+ $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
+
+ if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
+ die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
+ }
+ $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
+
+ $g_systemName = $g_targetObj->getSystemName();
+ if (length($g_systemName) == 0) {
+ die "The SYSTEM_NAME attribute is not set on the system target.\n";
+ }
+}
+
+
#Returns a hash that represents the 'aliases' node.
#Will look like:
# aliases {
@@ -466,10 +494,10 @@
$node{$name}{status} = "okay";
if ($ncsi == 1) {
- $node{$name}{"use-ncsi"} = STANDALONE_PROPERTY;
+ $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
}
if ($hwChecksum == 0) {
- $node{$name}{"no-hw-checksum"} = STANDALONE_PROPERTY;
+ $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
}
$node{$name}{COMMENT} = "$eth->{SOURCE} ->\n$eth->{DEST_PARENT}";
@@ -500,6 +528,186 @@
return @nodes;
}
+#Returns a list of hashes that represent the I2C device nodes.
+#There is 1 parent node for each bus, which then have subnodes
+#for each device on that bus. If a bus doesn't have any
+#attached devices, it doesn't need to show up.
+#The nodes will look like:
+# &i2c0 {
+# status = "okay"
+# device1@addr { (addr = 7 bit I2C address)
+# reg = <addr>
+# compatible = ...
+# ...
+# }
+# device2@addr {
+# reg = <addr>
+# ...
+# }
+# }
+# &i2c1 {
+# ...
+# }
+sub getI2CNodes()
+{
+ my @nodes;
+ my %busNodes;
+
+ my $connections = findConnections($g_bmc, "I2C");
+
+ if ($connections eq "") {
+ print "WARNING: No I2C buses found connected to the BMC\n";
+ return @nodes;
+ }
+
+ foreach my $i2c (@{$connections->{CONN}}) {
+
+ my %deviceNode, my $deviceName;
+
+ $deviceNode{COMMENT} = "$i2c->{SOURCE} ->\n$i2c->{DEST_PARENT}";
+
+ $deviceName = lc $i2c->{DEST_PARENT};
+ $deviceName =~ s/-\d+$//; #remove trailing position
+ $deviceName =~ s/.*\///; #remove the front of the path
+
+ #Get the I2C address
+ my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
+ $i2cAddress = hex($i2cAddress);
+ if ($i2cAddress == 0) {
+ die "ERROR: Missing I2C address on $i2c->{DEST}\n";
+ }
+
+ #Put it in the format we want to print it in
+ $i2cAddress = adjustI2CAddress($i2cAddress);
+ $deviceNode{reg} = "<$i2cAddress>";
+
+ $deviceName = makeNodeName($deviceName, $deviceNode{reg});
+
+ #Get the I2C bus number
+ if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
+ "I2C_PORT")) {
+ die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
+ "is either missing or empty.\n";
+ }
+
+ my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
+ if ($busNum =~ /0x/i) {
+ $busNum = hex($busNum);
+ }
+
+ #Convert the number to the Linux numbering scheme.
+ $busNum = adjustI2CBusNum($busNum);
+
+ #Get the compatible property
+ if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
+ "BMC_DT_COMPATIBLE")) {
+ die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
+ "is either missing or empty.\n";
+ }
+
+ $deviceNode{compatible} = $g_targetObj->getAttribute(
+ $i2c->{DEST_PARENT},
+ "BMC_DT_COMPATIBLE");
+
+ #Get any other part specific properties, where the property
+ #names are actually defined in the XML.
+ my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
+ foreach my $prop (sort keys %props) {
+ $deviceNode{$prop} = $props{$prop};
+ }
+
+ #busNodeName is the hash twice so when we loop
+ #below it doesn't get lost
+ my $busNodeName = "i2c$busNum";
+ $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
+ $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
+ }
+
+ #Each bus gets its own hash entry in the array
+ for my $b (sort keys %busNodes) {
+ push @nodes, { %{$busNodes{$b}} };
+ }
+
+ return @nodes;
+}
+
+
+#Returns a hash of property names and values that should be stored in
+#the device tree node for this device. The names of the properties and
+#the attributes to find their values in are stored in the
+#BMC_DT_ATTR_NAMES attribute in the chip.
+# $chip = the chip target
+sub getPartDefinedDTProperties()
+{
+ my $chip = shift;
+ my %props;
+
+ if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
+ return %props;
+ }
+
+ my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
+ $attr =~ s/\s//g;
+ my @names = split(',', $attr);
+
+ #There can be up to 4 entries in this attribute
+ for (my $i = 0; $i < scalar @names; $i += 2) {
+
+ #$names[$i] holds the name of the attribute.
+ #$names[$i+1] holds the name of the property to store its value in.
+ if (($names[$i] ne "NA") && ($names[$i] ne "")) {
+
+ my $val = $g_targetObj->getAttribute($chip, $names[$i]);
+
+ #if the value is empty, assume it's for a standalone property,
+ #which gets turned into: some-property;
+ if ($val eq "") {
+ $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
+ }
+ else {
+ $props{$names[$i+1]} = "<$val>";
+ }
+ }
+ }
+
+ return %props;
+}
+
+
+#Convert the MRW I2C address into the format the dts needs
+# $addr = the I2C Address
+sub adjustI2CAddress()
+{
+ my $addr = shift;
+
+ #MRW holds the 8 bit value. We need the 7 bit one.
+ my $addr = $addr >> 1;
+ $addr = sprintf("0x%X", $addr);
+ $addr = lc $addr;
+
+ return $addr;
+}
+
+
+#Converts the I2C Bus number from the one in the MRW/schematics to the
+#kernel based number. Usually involves subtracting 1.
+# $num = I2C Bus number from the MRW
+sub adjustI2CBusNum()
+{
+ my $num = shift;
+
+ if (exists $I2C_BUS_ADJUST{$g_bmcModel}) {
+ my $op = $I2C_BUS_ADJUST{$g_bmcModel};
+ $num = $num + $op;
+ }
+ else {
+ print "WARNING: No I2C Bus number adjustment done " .
+ "for this BMC model\n";
+ }
+
+ return $num;
+}
+
#Returns a hash{'reg'} = "<.....>" based on the
#BMC_DT_MEMORY attribute. This is used to display
@@ -595,7 +803,7 @@
# if the key is:
# - 'DTSI_INCLUDE', then value gets turned into a #include
# - 'COMMENT', then value gets turned into a // comment
-# - 'STANDALONE_PROPERTY' then value gets turned into: value;
+# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
#
# If the value is:
# - a hash - then that hash gets turned into a child node
@@ -621,16 +829,40 @@
print $f indent($level) . "$name {\n";
+ #First print properties, then includes, then subnodes
+
+ #Print Properties
foreach my $v (sort keys %vals) {
next if ($v eq "COMMENT");
+ next if ($v eq "DTSI_INCLUDE");
+ next if (ref($vals{$v}) eq "HASH");
+ next if (ref($vals{$v}) eq "ARRAY");
- #A header file include, print it later
- if ($v eq DTSI_INCLUDE) {
- $include = $vals{$v};
+ if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
+ printProperty($f, $level+1, $v, $vals{$v});
}
- #A nested node
- elsif (ref($vals{$v}) eq "HASH") {
+ else {
+ printZeroLengthProperty($f, $level+1, $v);
+ }
+ }
+
+ #Print Includes
+ foreach my $v (sort keys %vals) {
+
+ if ($v eq "DTSI_INCLUDE") {
+ #print 1 include per line
+ my @incs = split(',', $vals{$v});
+ foreach my $i (@incs) {
+ print $f qq(#include "$i";\n);
+ }
+ }
+ }
+
+ #Print Nodes
+ foreach my $v (sort keys %vals) {
+
+ if (ref($vals{$v}) eq "HASH") {
printNode($f, $level+1, $v, %{$vals{$v}});
}
#An array of nested nodes
@@ -638,20 +870,6 @@
my @array = @{$vals{$v}};
&printNodes($f, $level+1, @array);
}
- elsif ($vals{$v} ne STANDALONE_PROPERTY) {
- printProperty($f, $level+1, $v, $vals{$v});
- }
- else {
- printStandaloneProperty($f, $level+1, $v);
- }
- }
-
- #Now print the includes, if any.
- if ($include ne "") {
- my @incs = split(',', $include);
- foreach my $i (@incs) {
- print $f "#include \"$i\";\n";
- }
}
print $f indent($level) . "};\n";
@@ -789,6 +1007,27 @@
return @includes;
}
+#Appends the first value of the 'reg' property
+#passed in to the name passed in to create the
+#full name for the node
+# $name = node name that will be appended to
+# $reg = the reg property values
+sub makeNodeName()
+{
+ my ($name, $reg) = @_;
+
+ $reg =~ s/<//g;
+ $reg =~ s/>//g;
+ my @vals = split(' ', $reg);
+
+ if (scalar @vals > 0) {
+ $vals[0] =~ s/0x//;
+ $name .= "@" . lc $vals[0];
+ }
+
+ return $name;
+}
+
#Prints the root node starting bracket.
# $f = file handle