util/sconfig: Add support for discontiguous FW_CONFIG fields

Sooner or later, some board was going to need extra FW_CONFIG bits for
a field that was already in production, so this patch adds support for
adding extra (unused) bits to a field.

The extra are appended via a syntax like:
`field FIELD_NAME START0 END0 | START1 END1 | START2 END2 ...`
and the suffixed bits are all treated as if they are contiguous when
defining option values.

BUG=b:185190978
TEST=Modified volteer fw_config to the following:
field AUDIO 8 10 | 29 29 | 31 31
        option NONE 0
	option MAX98357_ALC5682I_I2S 1
        option MAX98373_ALC5682I_I2S 2
	option MAX98373_ALC5682_SNDW 3
        option MAX98373_ALC5682I_I2S_UP4 4
        option MAX98360_ALC5682I_I2S 5
        option RT1011_ALC5682I_I2S 6
        option AUDIO_FOO 7
	option AUDIO_BAR 8
        option AUDIO_QUUX 9
        option AUDIO_BLAH1 10
        option AUDIO_BLAH2 15
        option AUDIO_BLAH3 16
        option AUDIO_BLAH4 31
end

which yielded (in static_fw_config.h):
 FW_CONFIG_FIELD_AUDIO_MASK 0xa0000700
 FW_CONFIG_FIELD_AUDIO_OPTION_NONE_VALUE 0x0
 FW_CONFIG_FIELD_AUDIO_OPTION_MAX98357_ALC5682I_I2S_VALUE 0x100
 FW_CONFIG_FIELD_AUDIO_OPTION_MAX98373_ALC5682I_I2S_VALUE 0x200
 FW_CONFIG_FIELD_AUDIO_OPTION_MAX98373_ALC5682_SNDW_VALUE 0x300
 FW_CONFIG_FIELD_AUDIO_OPTION_MAX98373_ALC5682I_I2S_UP4_VALUE 0x400
 FW_CONFIG_FIELD_AUDIO_OPTION_MAX98360_ALC5682I_I2S_VALUE 0x500
 FW_CONFIG_FIELD_AUDIO_OPTION_RT1011_ALC5682I_I2S_VALUE 0x600
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_FOO_VALUE 0x700
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAR_VALUE 0x20000000
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_QUUX_VALUE 0x20000100
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH1_VALUE 0x20000200
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH2_VALUE 0x20000700
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH3_VALUE 0x80000000
 FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH4_VALUE 0xa0000700

Change-Id: I5ed76706347ee9642198efc77139abdc3af1b8a6
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/52747
Reviewed-by: Duncan Laurie <duncan@iceblink.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/Documentation/lib/fw_config.md b/Documentation/lib/fw_config.md
index dcf1bb4e..d5f0bb2 100644
--- a/Documentation/lib/fw_config.md
+++ b/Documentation/lib/fw_config.md
@@ -121,12 +121,48 @@
 location in the bitmask.  Field names must be at least three characters long in order to
 satisfy the sconfig parser requirements and they must be unique with non-overlapping masks.
 
-    field <name> <start-bit> <end-bit> [option...] end
+	field <name> <start-bit> <end-bit> [option...] end
 
 For single-bit fields only one number is needed:
 
 	field <name> <bit> [option...] end
 
+A field definition can also contain multiple sets of bit masks, which can be dis-contiguous.
+They are treated as if they are contiguous when defining option values.  This allows for
+extending fields even after the bits after its current masks are occupied.
+
+	field <name> <start-bit0> <end-bit0> | <start-bit1> <end-bit1> | ...
+
+For example, if more audio options need to be supported:
+
+	field AUDIO 3 3
+		option AUDIO_0  0
+		option AUDIO_1  1
+	end
+	field OTHER 4 4
+		...
+	end
+
+the following can be done:
+
+	field AUDIO 3 3 | 5 5
+		option AUDIO_FOO   0
+		option AUDIO_BLAH  1
+		option AUDIO_BAR   2
+		option AUDIO_BAZ   3
+	end
+	field OTHER 4 4
+		...
+	end
+
+In that case, the AUDIO masks are extended like so:
+
+	#define FW_CONFIG_FIELD_AUDIO_MASK						0x28
+	#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_FOO_VALUE	0x0
+	#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BLAH_VALUE	0x8
+	#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAR_VALUE	0x20
+	#define FW_CONFIG_FIELD_AUDIO_OPTION_AUDIO_BAz_VALUE	0x28
+
 Each `field` definition starts a new block that can be composed of zero or more field options,
 and it is terminated with `end`.
 
diff --git a/util/sconfig/lex.yy.c_shipped b/util/sconfig/lex.yy.c_shipped
index 3d6b0c8..f62bada 100644
--- a/util/sconfig/lex.yy.c_shipped
+++ b/util/sconfig/lex.yy.c_shipped
@@ -31,7 +31,7 @@
 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
 
 /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
- * if you want the limit (max/min) macros for int types.
+ * if you want the limit (max/min) macros for int types. 
  */
 #ifndef __STDC_LIMIT_MACROS
 #define __STDC_LIMIT_MACROS 1
@@ -48,7 +48,7 @@
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
 typedef int flex_int32_t;
-typedef unsigned char flex_uint8_t;
+typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
 
@@ -159,10 +159,10 @@
 #define EOB_ACT_CONTINUE_SCAN 0
 #define EOB_ACT_END_OF_FILE 1
 #define EOB_ACT_LAST_MATCH 2
-
+    
     #define YY_LESS_LINENO(n)
     #define YY_LINENO_REWIND_TO(ptr)
-
+    
 /* Return all but the first "n" matched characters back to the input stream. */
 #define yyless(n) \
 	do \
@@ -349,8 +349,8 @@
 	(yy_hold_char) = *yy_cp; \
 	*yy_cp = '\0'; \
 	(yy_c_buf_p) = yy_cp;
-#define YY_NUM_RULES 50
-#define YY_END_OF_BUFFER 51
+#define YY_NUM_RULES 51
+#define YY_END_OF_BUFFER 52
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -358,31 +358,31 @@
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static const flex_int16_t yy_accept[206] =
+static const flex_int16_t yy_accept[207] =
     {   0,
-        0,    0,   51,   49,    1,    3,   49,   49,   49,   44,
-       44,   42,   45,   49,   45,   45,   45,   45,   45,   49,
-       49,   49,   49,   49,   49,   49,   49,   49,   49,   49,
-        1,    3,   49,    0,   49,   49,    0,    2,   44,   45,
-       49,   49,   49,    9,   49,   49,   45,   49,   49,   49,
-       49,   49,   49,   49,   49,   49,   49,   49,   36,   49,
-       49,   49,   49,   49,   49,   15,   49,   49,   49,   49,
-       49,   49,   49,   49,   49,   48,   48,   49,    0,   43,
-       49,   49,   49,   25,   49,   49,   35,   40,   49,   49,
-       49,   49,   49,   49,   22,   49,   49,   34,   49,   31,
+        0,    0,   52,   50,    1,    3,   50,   50,   50,   45,
+       45,   42,   46,   50,   46,   46,   46,   46,   46,   50,
+       50,   50,   50,   50,   50,   50,   50,   50,   50,   43,
+       50,    1,    3,   50,    0,   50,   50,    0,    2,   45,
+       46,   50,   50,   50,    9,   50,   50,   46,   50,   50,
+       50,   50,   50,   50,   50,   50,   50,   50,   50,   36,
+       50,   50,   50,   50,   50,   50,   15,   50,   50,   50,
+       50,   50,   50,   50,   50,   50,   49,   49,   50,    0,
+       44,   50,   50,   50,   25,   50,   50,   35,   40,   50,
+       50,   50,   50,   50,   50,   22,   50,   50,   34,   50,
 
-       49,   49,   16,   49,   19,   21,   49,    8,   49,   49,
-       29,   49,   30,    7,   49,    0,   46,   49,    4,   49,
-       49,   49,   32,   49,   49,   49,   33,   49,   49,   49,
-       49,   49,   28,   49,   49,   49,   49,   49,   47,   47,
-        6,   49,   49,   49,   12,   49,   49,   49,   49,   49,
-       23,   49,   49,   14,   49,   49,   49,   49,    5,   26,
-       49,   49,   17,   49,   20,   49,   13,   49,   49,   49,
-       49,   49,   27,   38,   49,   49,   49,   49,   49,   49,
-       49,   49,   49,   10,   49,   49,   49,   11,   49,   18,
-       49,   49,   49,   37,   49,   49,   24,   49,   39,   49,
+       31,   50,   50,   16,   50,   19,   21,   50,    8,   50,
+       50,   29,   50,   30,    7,   50,    0,   47,   50,    4,
+       50,   50,   50,   32,   50,   50,   50,   33,   50,   50,
+       50,   50,   50,   28,   50,   50,   50,   50,   50,   48,
+       48,    6,   50,   50,   50,   12,   50,   50,   50,   50,
+       50,   23,   50,   50,   14,   50,   50,   50,   50,    5,
+       26,   50,   50,   17,   50,   20,   50,   13,   50,   50,
+       50,   50,   50,   27,   38,   50,   50,   50,   50,   50,
+       50,   50,   50,   50,   10,   50,   50,   50,   11,   50,
+       18,   50,   50,   50,   37,   50,   50,   24,   50,   39,
 
-       49,   49,   49,   41,    0
+       50,   50,   50,   50,   41,    0
     } ;
 
 static const YY_CHAR yy_ec[256] =
@@ -400,7 +400,7 @@
 
        21,   22,   23,   24,   25,    1,    1,   26,   27,   28,
        29,   30,   31,   32,   33,   34,   35,   36,   37,   38,
-       39,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+       39,    1,    1,   40,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -417,146 +417,146 @@
         1,    1,    1,    1,    1
     } ;
 
-static const YY_CHAR yy_meta[40] =
+static const YY_CHAR yy_meta[41] =
     {   0,
         1,    2,    2,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,    1,    1
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1
     } ;
 
-static const flex_int16_t yy_base[213] =
+static const flex_int16_t yy_base[214] =
     {   0,
-        0,    0,  278,    0,  275,  279,  273,   38,   42,   39,
-      237,    0,   45,  260,   55,   59,   83,   65,   62,   54,
-      248,   68,   25,   59,   73,   86,  251,   81,  238,    0,
-      268,  279,   77,  264,  104,  117,  265,  279,    0,  114,
-      117,  252,  241,    0,  240,  229,  123,  236,  231,  241,
-      230,  238,  242,  229,  231,  235,  235,  229,  235,  220,
-      220,  230,  220,  222,  224,    0,  211,  219,  213,  213,
-      118,  223,  215,  221,   92,    0,  279,  140,  233,    0,
-      226,  219,  205,  218,  208,  215,    0,    0,  206,  204,
-      210,  207,  198,  206,    0,  204,  194,    0,  198,    0,
+        0,    0,  279,    0,  276,  280,  274,   39,   43,   40,
+      238,    0,   46,  261,   56,   60,   64,   67,   72,   56,
+      249,   74,   26,   61,   77,   82,  252,   81,  239,    0,
+        0,  269,  280,   89,  265,  110,  115,  266,  280,    0,
+      112,  115,  253,  242,    0,  241,  230,  121,  237,  232,
+      242,  231,  239,  243,  230,  232,  236,  236,  230,  236,
+      221,  221,  231,  221,  223,  225,    0,  212,  220,  214,
+      214,  116,  224,  216,  222,  122,    0,  280,  139,  234,
+        0,  227,  220,  206,  219,  209,  216,    0,    0,  207,
+      205,  211,  208,  199,  207,    0,  205,  195,    0,  199,
 
-      202,  192,    0,  195,    0,    0,  201,    0,  193,  192,
-        0,  183,    0,    0,  210,  209,    0,  180,    0,  193,
-      192,  185,    0,  189,  179,  175,    0,  185,  173,  179,
-      184,  185,    0,  172,  179,  166,  169,  158,    0,  279,
-        0,  170,  174,  166,    0,  165,  167,  163,  165,  170,
-        0,  154,  159,    0,  152,  152,  151,  148,    0,    0,
-      160,  162,    0,  146,  163,  149,    0,  156,  160,  141,
-      141,  148,    0,    0,  147,  126,  125,  123,  134,  120,
-      130,  120,  112,    0,  124,  122,  127,    0,  116,    0,
-      117,  119,  101,    0,   93,   97,    0,   86,    0,   74,
+        0,  203,  193,    0,  196,    0,    0,  202,    0,  194,
+      193,    0,  184,    0,    0,  211,  210,    0,  181,    0,
+      194,  193,  186,    0,  190,  180,  176,    0,  186,  174,
+      180,  185,  186,    0,  173,  180,  167,  170,  159,    0,
+      280,    0,  171,  175,  167,    0,  166,  168,  164,  166,
+      171,    0,  155,  160,    0,  153,  153,  152,  149,    0,
+        0,  161,  163,    0,  147,  164,  150,    0,  157,  161,
+      142,  142,  149,    0,    0,  148,  140,  139,  137,  135,
+      121,  131,  121,  113,    0,  125,  123,  128,    0,  117,
+        0,  118,  121,  113,    0,   97,  105,    0,   88,    0,
 
-       64,   45,   46,    0,  279,   48,  159,  161,  163,  165,
-      167,  169
+       78,   66,   37,   47,    0,  280,   49,  157,  159,  161,
+      163,  165,  167
     } ;
 
-static const flex_int16_t yy_def[213] =
+static const flex_int16_t yy_def[214] =
     {   0,
-      205,    1,  205,  206,  205,  205,  206,  207,  208,  206,
-       10,  206,   10,  206,   10,   10,   10,   10,   10,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      205,  205,  207,  209,  210,  208,  211,  205,   10,   10,
-       10,  206,  206,  206,  206,  206,   10,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  205,  210,  212,   41,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      206,    1,  206,  207,  206,  206,  207,  208,  209,  207,
+       10,  207,   10,  207,   10,   10,   10,   10,   10,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  206,  206,  208,  210,  211,  209,  212,  206,   10,
+       10,   10,  207,  207,  207,  207,  207,   10,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  206,  211,  213,
+       42,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
 
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  205,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  205,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
-      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  206,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      206,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
+      207,  207,  207,  207,  207,  207,  207,  207,  207,  207,
 
-      206,  206,  206,  206,    0,  205,  205,  205,  205,  205,
-      205,  205
+      207,  207,  207,  207,  207,    0,  206,  206,  206,  206,
+      206,  206,  206
     } ;
 
-static const flex_int16_t yy_nxt[319] =
+static const flex_int16_t yy_nxt[321] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   10,   12,
        13,   13,   14,    4,    4,    4,   15,   13,   16,   17,
        18,   19,   20,   21,   22,   23,   24,    4,   25,   26,
-        4,   27,   28,    4,   29,    4,    4,    4,    4,   34,
-       34,   61,   35,   37,   38,   39,   39,   39,   30,   40,
-       40,   40,   40,   40,   62,   40,   40,   40,   40,   40,
-       40,   40,   40,   40,  204,   40,   40,   40,   40,   40,
-       40,   40,   40,   40,   54,   63,   57,  203,   34,   34,
-       43,   76,   45,   55,  202,   64,   52,   44,   46,   40,
-       40,   40,   50,  201,   65,   58,   59,   51,   53,   60,
+        4,   27,   28,    4,   29,    4,    4,    4,    4,   30,
+       35,   35,   62,   36,   38,   39,   40,   40,   40,   31,
+       41,   41,   41,   41,   41,   63,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,  205,   41,   41,   41,  204,
+       41,   41,   41,   41,   41,   41,   55,   64,   41,   41,
+       41,   44,   58,   46,   48,   56,  203,   65,   45,   47,
+       35,   35,   49,   77,   51,   50,   53,  202,   66,   52,
 
-       66,  200,   67,   47,   68,   79,   79,   72,   30,  113,
-       73,   48,  114,   69,   49,   74,  199,   70,   37,   38,
-       40,   40,   40,   80,   80,   80,  198,   80,   80,   40,
-       40,   40,  197,   80,   80,   80,   80,   80,   80,  108,
-      109,   79,   79,  196,  115,  195,  194,  193,  192,  191,
-      190,  189,  188,  187,  186,  185,  184,  183,   85,   33,
-       33,   36,   36,   34,   34,   78,   78,   37,   37,   79,
-       79,  182,  181,  180,  179,  178,  177,  176,  175,  174,
-      173,  172,  171,  170,  169,  168,  167,  166,  165,  164,
-      163,  162,  161,  160,  159,  158,  157,  156,  155,  154,
+       69,   59,   60,  201,   67,   61,   68,   73,   54,   70,
+       74,   80,   80,   71,   31,   75,   38,   39,   41,   41,
+       41,   81,   81,   81,  200,   81,   81,   41,   41,   41,
+      199,   81,   81,   81,   81,   81,   81,  109,  110,  114,
+       80,   80,  115,  116,  198,  197,  196,  195,  194,  193,
+      192,  191,  190,  189,  188,  187,   86,   34,   34,   37,
+       37,   35,   35,   79,   79,   38,   38,   80,   80,  186,
+      185,  184,  183,  182,  181,  180,  179,  178,  177,  176,
+      175,  174,  173,  172,  171,  170,  169,  168,  167,  166,
+      165,  164,  163,  162,  161,  160,  159,  158,  157,  156,
 
-      153,  152,  151,  150,  149,  148,  147,  146,  145,  144,
-      143,  142,  141,  140,  139,  138,  137,  136,  135,  134,
-      133,  132,  131,  130,  129,  128,  127,  126,  125,  124,
-      123,  122,  121,  120,  119,  118,  117,  116,  112,  111,
-      110,  107,  106,  105,  104,  103,  102,  101,  100,   99,
-       98,   97,   96,   95,   94,   93,   92,   91,   90,   89,
-       88,   87,   86,   84,   83,   82,   81,   38,   77,   31,
-       75,   71,   56,   42,   41,   32,   31,  205,    3,  205,
-      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
-      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      155,  154,  153,  152,  151,  150,  149,  148,  147,  146,
+      145,  144,  143,  142,  141,  140,  139,  138,  137,  136,
+      135,  134,  133,  132,  131,  130,  129,  128,  127,  126,
+      125,  124,  123,  122,  121,  120,  119,  118,  117,  113,
+      112,  111,  108,  107,  106,  105,  104,  103,  102,  101,
+      100,   99,   98,   97,   96,   95,   94,   93,   92,   91,
+       90,   89,   88,   87,   85,   84,   83,   82,   39,   78,
+       32,   76,   72,   57,   43,   42,   33,   32,  206,    3,
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
 
-      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
-      205,  205,  205,  205,  205,  205,  205,  205
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206
     } ;
 
-static const flex_int16_t yy_chk[319] =
+static const flex_int16_t yy_chk[321] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,    1,    1,    8,
-        8,   23,    8,    9,    9,   10,   10,   10,  206,   10,
-       10,   13,   13,   13,   23,   10,   10,   10,   10,   10,
-       10,   15,   15,   15,  203,   16,   16,   16,   19,   19,
-       19,   18,   18,   18,   20,   24,   22,  202,   33,   33,
-       15,   33,   16,   20,  201,   24,   19,   15,   16,   17,
-       17,   17,   18,  200,   25,   22,   22,   18,   19,   22,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        8,    8,   23,    8,    9,    9,   10,   10,   10,  207,
+       10,   10,   13,   13,   13,   23,   10,   10,   10,   10,
+       10,   10,   15,   15,   15,  204,   16,   16,   16,  203,
+       17,   17,   17,   18,   18,   18,   20,   24,   19,   19,
+       19,   15,   22,   16,   17,   20,  202,   24,   15,   16,
+       34,   34,   17,   34,   18,   17,   19,  201,   25,   18,
 
-       25,  198,   25,   17,   26,   35,   35,   28,   35,   75,
-       28,   17,   75,   26,   17,   28,  196,   26,   36,   36,
-       40,   40,   40,   41,   41,   41,  195,   41,   41,   47,
-       47,   47,  193,   41,   41,   41,   41,   41,   41,   71,
-       71,   78,   78,  192,   78,  191,  189,  187,  186,  185,
-      183,  182,  181,  180,  179,  178,  177,  176,   47,  207,
-      207,  208,  208,  209,  209,  210,  210,  211,  211,  212,
-      212,  175,  172,  171,  170,  169,  168,  166,  165,  164,
-      162,  161,  158,  157,  156,  155,  153,  152,  150,  149,
-      148,  147,  146,  144,  143,  142,  138,  137,  136,  135,
+       26,   22,   22,  199,   25,   22,   25,   28,   19,   26,
+       28,   36,   36,   26,   36,   28,   37,   37,   41,   41,
+       41,   42,   42,   42,  197,   42,   42,   48,   48,   48,
+      196,   42,   42,   42,   42,   42,   42,   72,   72,   76,
+       79,   79,   76,   79,  194,  193,  192,  190,  188,  187,
+      186,  184,  183,  182,  181,  180,   48,  208,  208,  209,
+      209,  210,  210,  211,  211,  212,  212,  213,  213,  179,
+      178,  177,  176,  173,  172,  171,  170,  169,  167,  166,
+      165,  163,  162,  159,  158,  157,  156,  154,  153,  151,
+      150,  149,  148,  147,  145,  144,  143,  139,  138,  137,
 
-      134,  132,  131,  130,  129,  128,  126,  125,  124,  122,
-      121,  120,  118,  116,  115,  112,  110,  109,  107,  104,
-      102,  101,   99,   97,   96,   94,   93,   92,   91,   90,
-       89,   86,   85,   84,   83,   82,   81,   79,   74,   73,
-       72,   70,   69,   68,   67,   65,   64,   63,   62,   61,
-       60,   59,   58,   57,   56,   55,   54,   53,   52,   51,
-       50,   49,   48,   46,   45,   43,   42,   37,   34,   31,
-       29,   27,   21,   14,   11,    7,    5,    3,  205,  205,
-      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
-      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      136,  135,  133,  132,  131,  130,  129,  127,  126,  125,
+      123,  122,  121,  119,  117,  116,  113,  111,  110,  108,
+      105,  103,  102,  100,   98,   97,   95,   94,   93,   92,
+       91,   90,   87,   86,   85,   84,   83,   82,   80,   75,
+       74,   73,   71,   70,   69,   68,   66,   65,   64,   63,
+       62,   61,   60,   59,   58,   57,   56,   55,   54,   53,
+       52,   51,   50,   49,   47,   46,   44,   43,   38,   35,
+       32,   29,   27,   21,   14,   11,    7,    5,    3,  206,
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
 
-      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
-      205,  205,  205,  205,  205,  205,  205,  205
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206,
+      206,  206,  206,  206,  206,  206,  206,  206,  206,  206
     } ;
 
 static yy_state_type yy_last_accepting_state;
@@ -638,9 +638,9 @@
 #endif
 
 #ifndef YY_NO_UNPUT
-
+    
     static void yyunput ( int c, char *buf_ptr  );
-
+    
 #endif
 
 #ifndef yytext_ptr
@@ -767,7 +767,7 @@
 	yy_state_type yy_current_state;
 	char *yy_cp, *yy_bp;
 	int yy_act;
-
+    
 	if ( !(yy_init) )
 		{
 		(yy_init) = 1;
@@ -821,13 +821,13 @@
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 206 )
+				if ( yy_current_state >= 207 )
 					yy_c = yy_meta[yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
 			++yy_cp;
 			}
-		while ( yy_base[yy_current_state] != 279 );
+		while ( yy_base[yy_current_state] != 280 );
 
 yy_find_action:
 		yy_act = yy_accept[yy_current_state];
@@ -1023,7 +1023,7 @@
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
-{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(NUMBER);}
+{return(PIPE);}
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
@@ -1035,12 +1035,11 @@
 	YY_BREAK
 case 46:
 YY_RULE_SETUP
-{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(PCIINT);}
+{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(NUMBER);}
 	YY_BREAK
 case 47:
-/* rule 47 can match eol */
 YY_RULE_SETUP
-{yylval.string = malloc(yyleng-1); strncpy(yylval.string, yytext+1, yyleng-2); yylval.string[yyleng-2]='\0'; return(STRING);}
+{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(PCIINT);}
 	YY_BREAK
 case 48:
 /* rule 48 can match eol */
@@ -1048,10 +1047,15 @@
 {yylval.string = malloc(yyleng-1); strncpy(yylval.string, yytext+1, yyleng-2); yylval.string[yyleng-2]='\0'; return(STRING);}
 	YY_BREAK
 case 49:
+/* rule 49 can match eol */
+YY_RULE_SETUP
+{yylval.string = malloc(yyleng-1); strncpy(yylval.string, yytext+1, yyleng-2); yylval.string[yyleng-2]='\0'; return(STRING);}
+	YY_BREAK
+case 50:
 YY_RULE_SETUP
 {yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(STRING);}
 	YY_BREAK
-case 50:
+case 51:
 YY_RULE_SETUP
 ECHO;
 	YY_BREAK
@@ -1337,7 +1341,7 @@
 {
 	yy_state_type yy_current_state;
 	char *yy_cp;
-
+    
 	yy_current_state = (yy_start);
 
 	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
@@ -1351,7 +1355,7 @@
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 206 )
+			if ( yy_current_state >= 207 )
 				yy_c = yy_meta[yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
@@ -1379,11 +1383,11 @@
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 206 )
+		if ( yy_current_state >= 207 )
 			yy_c = yy_meta[yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
-	yy_is_jam = (yy_current_state == 205);
+	yy_is_jam = (yy_current_state == 206);
 
 		return yy_is_jam ? 0 : yy_current_state;
 }
@@ -1393,7 +1397,7 @@
     static void yyunput (int c, char * yy_bp )
 {
 	char *yy_cp;
-
+    
     yy_cp = (yy_c_buf_p);
 
 	/* undo effects of setting up yytext */
@@ -1438,7 +1442,7 @@
 
 {
 	int c;
-
+    
 	*(yy_c_buf_p) = (yy_hold_char);
 
 	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
@@ -1505,12 +1509,12 @@
 
 /** Immediately switch to a different input stream.
  * @param input_file A readable stream.
- *
+ * 
  * @note This function does not reset the start condition to @c INITIAL .
  */
     void yyrestart  (FILE * input_file )
 {
-
+    
 	if ( ! YY_CURRENT_BUFFER ){
         yyensure_buffer_stack ();
 		YY_CURRENT_BUFFER_LVALUE =
@@ -1523,11 +1527,11 @@
 
 /** Switch to a different input buffer.
  * @param new_buffer The new input buffer.
- *
+ * 
  */
     void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
 {
-
+    
 	/* TODO. We should be able to replace this entire function body
 	 * with
 	 *		yypop_buffer_state();
@@ -1567,13 +1571,13 @@
 /** Allocate and initialize an input buffer state.
  * @param file A readable stream.
  * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
- *
+ * 
  * @return the allocated buffer state.
  */
     YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
 {
 	YY_BUFFER_STATE b;
-
+    
 	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state )  );
 	if ( ! b )
 		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
@@ -1596,11 +1600,11 @@
 
 /** Destroy the buffer.
  * @param b a buffer created with yy_create_buffer()
- *
+ * 
  */
     void yy_delete_buffer (YY_BUFFER_STATE  b )
 {
-
+    
 	if ( ! b )
 		return;
 
@@ -1621,7 +1625,7 @@
 
 {
 	int oerrno = errno;
-
+    
 	yy_flush_buffer( b );
 
 	b->yy_input_file = file;
@@ -1637,13 +1641,13 @@
     }
 
         b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
-
+    
 	errno = oerrno;
 }
 
 /** Discard all buffered characters. On the next scan, YY_INPUT will be called.
  * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
- *
+ * 
  */
     void yy_flush_buffer (YY_BUFFER_STATE  b )
 {
@@ -1672,7 +1676,7 @@
  *  the current state. This function will allocate the stack
  *  if necessary.
  *  @param new_buffer The new state.
- *
+ *  
  */
 void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
 {
@@ -1702,7 +1706,7 @@
 
 /** Removes and deletes the top of the stack, if present.
  *  The next element becomes the new top.
- *
+ *  
  */
 void yypop_buffer_state (void)
 {
@@ -1726,7 +1730,7 @@
 static void yyensure_buffer_stack (void)
 {
 	yy_size_t num_to_alloc;
-
+    
 	if (!(yy_buffer_stack)) {
 
 		/* First allocation is just for 2 elements, since we don't know if this
@@ -1769,13 +1773,13 @@
 /** Setup the input buffer state to scan directly from a user-specified character buffer.
  * @param base the character buffer
  * @param size the size in bytes of the character buffer
- *
+ * 
  * @return the newly allocated buffer state object.
  */
 YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
 {
 	YY_BUFFER_STATE b;
-
+    
 	if ( size < 2 ||
 	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
 	     base[size-1] != YY_END_OF_BUFFER_CHAR )
@@ -1804,14 +1808,14 @@
 /** Setup the input buffer state to scan a string. The next call to yylex() will
  * scan from a @e copy of @a str.
  * @param yystr a NUL-terminated string to scan
- *
+ * 
  * @return the newly allocated buffer state object.
  * @note If you want to scan bytes that may contain NUL values, then use
  *       yy_scan_bytes() instead.
  */
 YY_BUFFER_STATE yy_scan_string (const char * yystr )
 {
-
+    
 	return yy_scan_bytes( yystr, (int) strlen(yystr) );
 }
 
@@ -1819,7 +1823,7 @@
  * scan from a @e copy of @a bytes.
  * @param yybytes the byte buffer to scan
  * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
- *
+ * 
  * @return the newly allocated buffer state object.
  */
 YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len )
@@ -1828,7 +1832,7 @@
 	char *buf;
 	yy_size_t n;
 	int i;
-
+    
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = (yy_size_t) (_yybytes_len + 2);
 	buf = (char *) yyalloc( n  );
@@ -1882,16 +1886,16 @@
 /* Accessor  methods (get/set functions) to struct members. */
 
 /** Get the current line number.
- *
+ * 
  */
 int yyget_lineno  (void)
 {
-
+    
     return yylineno;
 }
 
 /** Get the input stream.
- *
+ * 
  */
 FILE *yyget_in  (void)
 {
@@ -1899,7 +1903,7 @@
 }
 
 /** Get the output stream.
- *
+ * 
  */
 FILE *yyget_out  (void)
 {
@@ -1907,7 +1911,7 @@
 }
 
 /** Get the length of the current token.
- *
+ * 
  */
 int yyget_leng  (void)
 {
@@ -1915,7 +1919,7 @@
 }
 
 /** Get the current token.
- *
+ * 
  */
 
 char *yyget_text  (void)
@@ -1925,18 +1929,18 @@
 
 /** Set the current line number.
  * @param _line_number line number
- *
+ * 
  */
 void yyset_lineno (int  _line_number )
 {
-
+    
     yylineno = _line_number;
 }
 
 /** Set the input stream. This does not discard the current
  * input buffer.
  * @param _in_str A readable stream.
- *
+ * 
  * @see yy_switch_to_buffer
  */
 void yyset_in (FILE *  _in_str )
@@ -1990,7 +1994,7 @@
 /* yylex_destroy is for both reentrant and non-reentrant scanners. */
 int yylex_destroy  (void)
 {
-
+    
     /* Pop the buffer stack, destroying each element. */
 	while(YY_CURRENT_BUFFER){
 		yy_delete_buffer( YY_CURRENT_BUFFER  );
@@ -2016,7 +2020,7 @@
 #ifndef yytext_ptr
 static void yy_flex_strncpy (char* s1, const char * s2, int n )
 {
-
+		
 	int i;
 	for ( i = 0; i < n; ++i )
 		s1[i] = s2[i];
@@ -2041,7 +2045,7 @@
 
 void *yyrealloc  (void * ptr, yy_size_t  size )
 {
-
+		
 	/* The cast to (char *) in the following accommodates both
 	 * implementations that use char* generic pointers, and those
 	 * that use void* generic pointers.  It works with the latter
diff --git a/util/sconfig/main.c b/util/sconfig/main.c
index 91407bc..111bb50 100644
--- a/util/sconfig/main.c
+++ b/util/sconfig/main.c
@@ -398,42 +398,77 @@
 	}
 }
 
-struct fw_config_field *new_fw_config_field(const char *name,
-					    unsigned int start_bit, unsigned int end_bit)
+void append_fw_config_bits(struct fw_config_field_bits **bits,
+			   unsigned int start_bit, unsigned int end_bit)
+{
+	struct fw_config_field_bits *new_bits = S_ALLOC(sizeof(*new_bits));
+	new_bits->start_bit = start_bit;
+	new_bits->end_bit = end_bit;
+	new_bits->next = NULL;
+
+	if (*bits == NULL) {
+		*bits = new_bits;
+		return;
+	}
+
+	struct fw_config_field_bits *tmp = *bits;
+	while (tmp->next)
+		tmp = tmp->next;
+
+	tmp->next = new_bits;
+}
+
+int fw_config_masks_overlap(struct fw_config_field *existing,
+			     unsigned int start_bit, unsigned int end_bit)
+{
+	struct fw_config_field_bits *bits = existing->bits;
+	while (bits) {
+		if (start_bit <= bits->end_bit && end_bit >= bits->start_bit) {
+			printf("ERROR: fw_config field [%u-%u] overlaps %s[%u-%u]\n",
+			       start_bit, end_bit,
+			       existing->name, bits->start_bit, bits->end_bit);
+			return 1;
+		}
+		bits = bits->next;
+	}
+
+	return 0;
+}
+
+struct fw_config_field *new_fw_config_field(const char *name, struct fw_config_field_bits *bits)
 {
 	struct fw_config_field *field = find_fw_config_field(name);
-
-	/* Check that field is within 64 bits. */
-	if (start_bit > end_bit || end_bit > 63) {
-		printf("ERROR: fw_config field %s has invalid range %u-%u\n", name,
-		       start_bit, end_bit);
-		exit(1);
-	}
+	struct fw_config_field_bits *tmp;
 
 	/* Don't allow re-defining a field, only adding new fields. */
 	if (field) {
-		printf("ERROR: fw_config field %s[%u-%u] already exists with range %u-%u\n",
-		       name, start_bit, end_bit, field->start_bit, field->end_bit);
+		printf("ERROR: fw_config field %s already exists\n", name);
 		exit(1);
 	}
 
-	/* Check for overlap with an existing field. */
-	field = fw_config_fields;
-	while (field) {
-		/* Check if the mask overlaps. */
-		if (start_bit <= field->end_bit && end_bit >= field->start_bit) {
-			printf("ERROR: fw_config field %s[%u-%u] overlaps %s[%u-%u]\n",
-			       name, start_bit, end_bit,
-			       field->name, field->start_bit, field->end_bit);
+	/* Check that each field is within 64 bits. */
+	tmp = bits;
+	while (tmp) {
+		if (tmp->start_bit > tmp->end_bit || tmp->end_bit > 63) {
+			printf("ERROR: fw_config field %s has invalid range %u-%u\n", field->name,
+			       tmp->start_bit, tmp->end_bit);
 			exit(1);
 		}
-		field = field->next;
+
+		/* Check for overlap with an existing field. */
+		struct fw_config_field *existing = fw_config_fields;
+		while (existing) {
+			if (fw_config_masks_overlap(existing, tmp->start_bit, tmp->end_bit))
+				exit(1);
+			existing = existing->next;
+		}
+
+		tmp = tmp->next;
 	}
 
 	field = S_ALLOC(sizeof(*field));
 	field->name = name;
-	field->start_bit = start_bit;
-	field->end_bit = end_bit;
+	field->bits = bits;
 	append_fw_config_field(field);
 
 	return field;
@@ -453,13 +488,25 @@
 	}
 }
 
+static uint64_t calc_max_field_value(const struct fw_config_field *field)
+{
+	unsigned int bit_count = 0;
+
+	const struct fw_config_field_bits *bits = field->bits;
+	while (bits) {
+		bit_count += 1 + bits->end_bit - bits->start_bit;
+		bits = bits->next;
+	};
+
+	return (1ull << bit_count) - 1ull;
+}
+
 void add_fw_config_option(struct fw_config_field *field, const char *name, uint64_t value)
 {
 	struct fw_config_option *option;
-	uint64_t field_max_value;
 
 	/* Check that option value fits within field mask. */
-	field_max_value = (1ull << (1ull + field->end_bit - field->start_bit)) - 1ull;
+	uint64_t field_max_value = calc_max_field_value(field);
 	if (value > field_max_value) {
 		printf("ERROR: fw_config option %s:%s value %" PRIx64 " larger than field max %"
 		       PRIx64 "\n",
@@ -525,6 +572,46 @@
 	append_fw_config_probe_to_dev(bus->dev, probe);
 }
 
+static uint64_t compute_fw_config_mask(const struct fw_config_field_bits *bits)
+{
+	uint64_t mask = 0;
+
+	while (bits) {
+		/* Compute mask from start and end bit. */
+		uint64_t tmp = ((1ull << (1ull + bits->end_bit - bits->start_bit)) - 1ull);
+		tmp <<= bits->start_bit;
+		mask |= tmp;
+		bits = bits->next;
+	}
+
+	return mask;
+}
+
+static unsigned int bits_width(const struct fw_config_field_bits *bits)
+{
+	return 1 + bits->end_bit - bits->start_bit;
+}
+
+static uint64_t calc_option_value(const struct fw_config_field *field,
+				  const struct fw_config_option *option)
+{
+	uint64_t value = 0;
+	uint64_t original = option->value;
+
+	struct fw_config_field_bits *bits = field->bits;
+	while (bits) {
+		const unsigned int width = bits_width(bits);
+		const uint64_t orig_mask = (1ull << width) - 1ull;
+		const uint64_t orig = (original & orig_mask);
+		value |= (orig << bits->start_bit);
+
+		original >>= width;
+		bits = bits->next;
+	}
+
+	return value;
+}
+
 static void emit_fw_config(FILE *fil)
 {
 	struct fw_config_field *field = fw_config_fields;
@@ -539,20 +626,16 @@
 		fprintf(fil, "#define FW_CONFIG_FIELD_%s_NAME \"%s\"\n",
 			field->name, field->name);
 
-		/* Compute mask from start and end bit. */
-		mask = ((1ull << (1ull + field->end_bit - field->start_bit)) - 1ull);
-		mask <<= field->start_bit;
-
+		mask = compute_fw_config_mask(field->bits);
 		fprintf(fil, "#define FW_CONFIG_FIELD_%s_MASK 0x%" PRIx64 "\n",
 			field->name, mask);
 
 		while (option) {
+			const uint64_t value = calc_option_value(field, option);
 			fprintf(fil, "#define FW_CONFIG_FIELD_%s_OPTION_%s_NAME \"%s\"\n",
 				field->name, option->name, option->name);
 			fprintf(fil, "#define FW_CONFIG_FIELD_%s_OPTION_%s_VALUE 0x%"
-				PRIx64 "\n", field->name, option->name,
-				option->value << field->start_bit);
-
+				PRIx64 "\n", field->name, option->name, value);
 			option = option->next;
 		}
 
@@ -1879,6 +1962,7 @@
 	if (override_devtree)
 		parse_override_devicetree(override_devtree, &override_root_dev);
 
+
 	FILE *autogen = fopen(outputc, "w");
 	if (!autogen) {
 		fprintf(stderr, "Could not open file '%s' for writing: ",
diff --git a/util/sconfig/sconfig.h b/util/sconfig/sconfig.h
index 0db1ce5..e6bd5aa 100644
--- a/util/sconfig/sconfig.h
+++ b/util/sconfig/sconfig.h
@@ -35,11 +35,18 @@
 	uint64_t value;
 	struct fw_config_option *next;
 };
+
+struct fw_config_field_bits;
+struct fw_config_field_bits {
+	unsigned int start_bit;
+	unsigned int end_bit;
+	struct fw_config_field_bits *next;
+};
+
 struct fw_config_field;
 struct fw_config_field {
 	const char *name;
-	unsigned int start_bit;
-	unsigned int end_bit;
+	struct fw_config_field_bits *bits;
 	struct fw_config_field *next;
 	struct fw_config_option *options;
 };
@@ -210,10 +217,15 @@
 
 struct fw_config_field *get_fw_config_field(const char *name);
 
-struct fw_config_field *new_fw_config_field(const char *name,
-					    unsigned int start_bit, unsigned int end_bit);
+void add_fw_config_field_bits(struct fw_config_field *field,
+			      unsigned int start_bit, unsigned int end_bit);
+
+struct fw_config_field *new_fw_config_field(const char *name, struct fw_config_field_bits *bits);
 
 void add_fw_config_option(struct fw_config_field *field, const char *name,
 			  uint64_t value);
 
 void add_fw_config_probe(struct bus *bus, const char *field, const char *option);
+
+void append_fw_config_bits(struct fw_config_field_bits **bits,
+			   unsigned int start_bit, unsigned int end_bit);
diff --git a/util/sconfig/sconfig.l b/util/sconfig/sconfig.l
index 5bdc52b..e5dc829 100755
--- a/util/sconfig/sconfig.l
+++ b/util/sconfig/sconfig.l
@@ -50,6 +50,7 @@
 end			{return(END);}
 smbios_slot_desc	{return(SLOT_DESC);}
 =			{return(EQUALS);}
+\|			{return(PIPE);}
 0x[0-9a-fA-F.]+		{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(NUMBER);}
 [0-9.]+			{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(NUMBER);}
 [0-9a-fA-F.]+		{yylval.string = malloc(yyleng+1); strncpy(yylval.string, yytext, yyleng); yylval.string[yyleng]='\0'; return(NUMBER);}
diff --git a/util/sconfig/sconfig.tab.c_shipped b/util/sconfig/sconfig.tab.c_shipped
index a8d1e1d..68421a1 100644
--- a/util/sconfig/sconfig.tab.c_shipped
+++ b/util/sconfig/sconfig.tab.c_shipped
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.7.2.  */
+/* A Bison parser, made by GNU Bison 3.7.5.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
    Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -45,11 +45,11 @@
    define necessary library symbols; they are noted "INFRINGES ON
    USER NAME SPACE" below.  */
 
-/* Identify Bison output.  */
-#define YYBISON 1
+/* Identify Bison output, and Bison version.  */
+#define YYBISON 30705
 
-/* Bison version.  */
-#define YYBISON_VERSION "3.7.2"
+/* Bison version string.  */
+#define YYBISON_VERSION "3.7.5"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -80,6 +80,7 @@
 static struct bus *cur_parent;
 static struct chip_instance *cur_chip_instance;
 static struct fw_config_field *cur_field;
+static struct fw_config_field_bits *cur_bits;
 
 
 
@@ -155,32 +156,35 @@
   YYSYMBOL_FW_CONFIG_FIELD = 43,           /* FW_CONFIG_FIELD  */
   YYSYMBOL_FW_CONFIG_OPTION = 44,          /* FW_CONFIG_OPTION  */
   YYSYMBOL_FW_CONFIG_PROBE = 45,           /* FW_CONFIG_PROBE  */
-  YYSYMBOL_YYACCEPT = 46,                  /* $accept  */
-  YYSYMBOL_devtree = 47,                   /* devtree  */
-  YYSYMBOL_chipchildren = 48,              /* chipchildren  */
-  YYSYMBOL_devicechildren = 49,            /* devicechildren  */
-  YYSYMBOL_chip = 50,                      /* chip  */
-  YYSYMBOL_51_1 = 51,                      /* @1  */
-  YYSYMBOL_device = 52,                    /* device  */
-  YYSYMBOL_53_2 = 53,                      /* @2  */
-  YYSYMBOL_54_3 = 54,                      /* @3  */
-  YYSYMBOL_alias = 55,                     /* alias  */
-  YYSYMBOL_status = 56,                    /* status  */
-  YYSYMBOL_resource = 57,                  /* resource  */
-  YYSYMBOL_reference = 58,                 /* reference  */
-  YYSYMBOL_registers = 59,                 /* registers  */
-  YYSYMBOL_subsystemid = 60,               /* subsystemid  */
-  YYSYMBOL_ioapic_irq = 61,                /* ioapic_irq  */
-  YYSYMBOL_smbios_slot_desc = 62,          /* smbios_slot_desc  */
-  YYSYMBOL_fw_config_table = 63,           /* fw_config_table  */
-  YYSYMBOL_fw_config_table_children = 64,  /* fw_config_table_children  */
-  YYSYMBOL_fw_config_field_children = 65,  /* fw_config_field_children  */
-  YYSYMBOL_fw_config_field = 66,           /* fw_config_field  */
-  YYSYMBOL_67_4 = 67,                      /* $@4  */
-  YYSYMBOL_68_5 = 68,                      /* $@5  */
-  YYSYMBOL_69_6 = 69,                      /* $@6  */
-  YYSYMBOL_fw_config_option = 70,          /* fw_config_option  */
-  YYSYMBOL_fw_config_probe = 71            /* fw_config_probe  */
+  YYSYMBOL_PIPE = 46,                      /* PIPE  */
+  YYSYMBOL_YYACCEPT = 47,                  /* $accept  */
+  YYSYMBOL_devtree = 48,                   /* devtree  */
+  YYSYMBOL_chipchildren = 49,              /* chipchildren  */
+  YYSYMBOL_devicechildren = 50,            /* devicechildren  */
+  YYSYMBOL_chip = 51,                      /* chip  */
+  YYSYMBOL_52_1 = 52,                      /* @1  */
+  YYSYMBOL_device = 53,                    /* device  */
+  YYSYMBOL_54_2 = 54,                      /* @2  */
+  YYSYMBOL_55_3 = 55,                      /* @3  */
+  YYSYMBOL_alias = 56,                     /* alias  */
+  YYSYMBOL_status = 57,                    /* status  */
+  YYSYMBOL_resource = 58,                  /* resource  */
+  YYSYMBOL_reference = 59,                 /* reference  */
+  YYSYMBOL_registers = 60,                 /* registers  */
+  YYSYMBOL_subsystemid = 61,               /* subsystemid  */
+  YYSYMBOL_ioapic_irq = 62,                /* ioapic_irq  */
+  YYSYMBOL_smbios_slot_desc = 63,          /* smbios_slot_desc  */
+  YYSYMBOL_fw_config_table = 64,           /* fw_config_table  */
+  YYSYMBOL_fw_config_table_children = 65,  /* fw_config_table_children  */
+  YYSYMBOL_fw_config_field_children = 66,  /* fw_config_field_children  */
+  YYSYMBOL_fw_config_field_bits = 67,      /* fw_config_field_bits  */
+  YYSYMBOL_fw_config_field_bits_repeating = 68, /* fw_config_field_bits_repeating  */
+  YYSYMBOL_fw_config_field = 69,           /* fw_config_field  */
+  YYSYMBOL_70_4 = 70,                      /* $@4  */
+  YYSYMBOL_71_5 = 71,                      /* $@5  */
+  YYSYMBOL_72_6 = 72,                      /* $@6  */
+  YYSYMBOL_fw_config_option = 73,          /* fw_config_option  */
+  YYSYMBOL_fw_config_probe = 74            /* fw_config_probe  */
 };
 typedef enum yysymbol_kind_t yysymbol_kind_t;
 
@@ -224,6 +228,18 @@
 typedef short yytype_int16;
 #endif
 
+/* Work around bug in HP-UX 11.23, which defines these macros
+   incorrectly for preprocessor constants.  This workaround can likely
+   be removed in 2023, as HPE has promised support for HP-UX 11.23
+   (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
+   <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>.  */
+#ifdef __hpux
+# undef UINT_LEAST8_MAX
+# undef UINT_LEAST16_MAX
+# define UINT_LEAST8_MAX 255
+# define UINT_LEAST16_MAX 65535
+#endif
+
 #if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
 typedef __UINT_LEAST8_TYPE__ yytype_uint8;
 #elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
@@ -321,9 +337,9 @@
 
 /* Suppress unused-variable warnings by "using" E.  */
 #if ! defined lint || defined __GNUC__
-# define YYUSE(E) ((void) (E))
+# define YY_USE(E) ((void) (E))
 #else
-# define YYUSE(E) /* empty */
+# define YY_USE(E) /* empty */
 #endif
 
 #if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
@@ -490,19 +506,19 @@
 /* YYFINAL -- State number of the termination state.  */
 #define YYFINAL  2
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   79
+#define YYLAST   83
 
 /* YYNTOKENS -- Number of terminals.  */
-#define YYNTOKENS  46
+#define YYNTOKENS  47
 /* YYNNTS -- Number of nonterminals.  */
-#define YYNNTS  26
+#define YYNNTS  28
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  50
+#define YYNRULES  53
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  89
+#define YYNSTATES  95
 
 /* YYMAXUTOK -- Last valid token kind.  */
-#define YYMAXUTOK   300
+#define YYMAXUTOK   301
 
 
 /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
@@ -546,19 +562,19 @@
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
       25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
       35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
-      45
+      45,    46
 };
 
 #if YYDEBUG
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
-static const yytype_int8 yyrline[] =
+static const yytype_uint8 yyrline[] =
 {
-       0,    25,    25,    25,    25,    27,    27,    27,    27,    27,
-      29,    29,    29,    29,    29,    29,    29,    29,    29,    31,
-      31,    40,    40,    48,    48,    56,    58,    62,    62,    64,
-      67,    70,    73,    76,    79,    82,    85,    88,    92,    95,
-      95,    98,    98,   101,   101,   107,   107,   113,   113,   119,
-     123
+       0,    26,    26,    26,    26,    28,    28,    28,    28,    28,
+      30,    30,    30,    30,    30,    30,    30,    30,    30,    32,
+      32,    41,    41,    49,    49,    57,    59,    63,    63,    65,
+      68,    71,    74,    77,    80,    83,    86,    89,    93,    96,
+      96,    99,    99,   102,   108,   108,   111,   110,   115,   115,
+     123,   123,   129,   133
 };
 #endif
 
@@ -581,11 +597,12 @@
   "SLOT_DESC", "IO", "NUMBER", "SUBSYSTEMID", "INHERIT", "IOAPIC_IRQ",
   "IOAPIC", "PCIINT", "GENERIC", "SPI", "USB", "MMIO", "LPC", "ESPI",
   "GPIO", "FW_CONFIG_TABLE", "FW_CONFIG_FIELD", "FW_CONFIG_OPTION",
-  "FW_CONFIG_PROBE", "$accept", "devtree", "chipchildren",
+  "FW_CONFIG_PROBE", "PIPE", "$accept", "devtree", "chipchildren",
   "devicechildren", "chip", "@1", "device", "@2", "@3", "alias", "status",
   "resource", "reference", "registers", "subsystemid", "ioapic_irq",
   "smbios_slot_desc", "fw_config_table", "fw_config_table_children",
-  "fw_config_field_children", "fw_config_field", "$@4", "$@5", "$@6",
+  "fw_config_field_children", "fw_config_field_bits",
+  "fw_config_field_bits_repeating", "fw_config_field", "$@4", "$@5", "$@6",
   "fw_config_option", "fw_config_probe", YY_NULLPTR
 };
 
@@ -605,11 +622,11 @@
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
      275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
      285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
-     295,   296,   297,   298,   299,   300
+     295,   296,   297,   298,   299,   300,   301
 };
 #endif
 
-#define YYPACT_NINF (-15)
+#define YYPACT_NINF (-21)
 
 #define yypact_value_is_default(Yyn) \
   ((Yyn) == YYPACT_NINF)
@@ -623,15 +640,16 @@
      STATE-NUM.  */
 static const yytype_int8 yypact[] =
 {
-     -15,     9,   -15,     0,   -15,   -15,   -15,   -15,   -11,   -15,
-     -15,     2,   -15,    45,    -8,    13,     6,    19,   -15,   -15,
-     -15,   -15,   -15,    10,   -15,    23,    12,    11,    36,   -15,
-     -15,    -7,    25,    39,    30,    37,   -15,    -6,   -15,    38,
-     -15,   -15,   -15,   -15,    40,    25,   -15,   -15,    -1,   -15,
-      24,   -15,   -15,   -15,   -15,   -15,    -3,   -15,    27,   -15,
-      41,    31,    32,    46,   -15,   -15,   -15,   -15,   -15,   -15,
-     -15,   -15,     1,    47,    48,    35,    33,    49,   -15,    42,
-      51,    43,    44,   -15,   -15,    52,   -15,   -15,   -15
+     -21,     9,   -21,     2,   -21,   -21,   -21,   -21,   -11,   -21,
+     -21,     8,   -21,    45,   -13,    10,    18,    19,   -21,   -21,
+     -21,   -21,   -21,    12,   -20,   -21,    22,    15,    25,    37,
+     -21,   -21,    24,   -21,    -7,    11,    41,    38,    39,    -6,
+      12,   -20,   -21,   -21,    40,   -21,   -21,   -21,   -21,    43,
+      11,   -21,   -21,   -21,   -21,    -1,    29,   -21,   -21,   -21,
+     -21,   -21,    -3,   -21,    32,   -21,    46,    33,    35,    48,
+     -21,   -21,   -21,   -21,   -21,   -21,   -21,   -21,     1,    51,
+      50,    42,    20,    52,   -21,    44,    53,    47,    54,   -21,
+     -21,    55,   -21,   -21,   -21
 };
 
   /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -640,30 +658,31 @@
 static const yytype_int8 yydefact[] =
 {
        2,     0,     1,     0,    40,     3,     4,    19,     0,     9,
-      38,     0,    39,     0,    47,     0,     0,     0,    20,     6,
-       5,     8,     7,    45,    42,     0,     0,     0,     0,    43,
-      42,     0,     0,    25,     0,     0,    42,     0,    48,     0,
-      41,    27,    28,    23,     0,     0,    31,    30,     0,    46,
-       0,    18,    26,    21,    44,    49,     0,    18,     0,    24,
-       0,     0,     0,     0,    11,    10,    12,    16,    13,    14,
-      15,    17,     0,     0,     0,     0,     0,     0,    22,     0,
-      37,    32,     0,    50,    29,    36,    33,    34,    35
+      38,     0,    39,     0,    50,     0,     0,     0,    20,     6,
+       5,     8,     7,    48,    45,    42,     0,     0,     0,     0,
+      43,    42,     0,    46,     0,     0,    25,     0,     0,     0,
+       0,    45,    42,    51,     0,    41,    27,    28,    23,     0,
+       0,    31,    30,    49,    44,     0,     0,    18,    26,    21,
+      47,    52,     0,    18,     0,    24,     0,     0,     0,     0,
+      11,    10,    12,    16,    13,    14,    15,    17,     0,     0,
+       0,     0,     0,     0,    22,     0,    37,    32,     0,    53,
+      29,    36,    33,    34,    35
 };
 
   /* YYPGOTO[NTERM-NUM].  */
 static const yytype_int8 yypgoto[] =
 {
-     -15,   -15,   -15,    15,    17,   -15,    57,   -15,   -15,   -15,
-      34,   -15,   -15,    62,   -15,   -15,   -15,   -15,   -15,   -14,
-     -15,   -15,   -15,   -15,   -15,   -15
+     -21,   -21,   -21,     5,    17,   -21,    61,   -21,   -21,   -21,
+      26,   -21,   -21,    62,   -21,   -21,   -21,   -21,   -21,    -8,
+      49,    36,   -21,   -21,   -21,   -21,   -21,   -21
 };
 
   /* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int8 yydefgoto[] =
 {
-      -1,     1,    13,    56,    64,     9,    65,    57,    51,    45,
-      43,    66,    21,    67,    68,    69,    70,     6,     8,    31,
-      12,    36,    30,    24,    40,    71
+       0,     1,    13,    62,    70,     9,    71,    63,    57,    50,
+      48,    72,    21,    73,    74,    75,    76,     6,     8,    34,
+      24,    33,    12,    42,    31,    25,    45,    77
 };
 
   /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
@@ -671,52 +690,55 @@
      number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_int8 yytable[] =
 {
-       3,    15,    16,    10,     3,    15,    16,    38,    49,     2,
-      58,    59,     3,    54,    58,    78,    37,     7,     5,    14,
-      25,    23,    48,    27,    60,    26,    34,    61,    60,    62,
-      19,    61,    11,    62,    41,    42,    28,    39,    39,    29,
-      32,    33,    63,    39,    35,    44,    63,    46,     3,    15,
-      16,     4,    17,    55,    47,    50,    73,    52,    74,    18,
-      75,    76,    79,    77,    81,    80,    83,    82,    85,    88,
-      20,    84,    72,    87,    86,    22,     0,     0,     0,    53
+       3,    15,    16,    10,     3,    15,    16,    43,    53,     2,
+      64,    65,     3,    60,    64,    84,    23,    26,     5,     7,
+      46,    47,    27,    39,    66,    14,    32,    67,    66,    68,
+      19,    67,    11,    68,    55,    28,    29,    44,    44,    35,
+      37,    30,    69,    44,    36,    38,    69,    49,     3,    15,
+      16,     4,    17,    40,    88,    51,    52,    56,    61,    18,
+      58,    79,    81,    80,    82,    83,    85,    86,    78,    89,
+      91,    87,    94,    90,    20,    22,    59,    54,    92,     0,
+       0,    41,     0,    93
 };
 
 static const yytype_int8 yycheck[] =
 {
        3,     4,     5,    14,     3,     4,     5,    14,    14,     0,
-      13,    14,     3,    14,    13,    14,    30,    17,     1,    17,
-       7,    29,    36,    17,    27,    12,    15,    30,    27,    32,
-      13,    30,    43,    32,     9,    10,    17,    44,    44,    29,
-      17,    29,    45,    44,     8,     6,    45,    17,     3,     4,
-       5,    42,     7,    29,    17,    17,    29,    17,    17,    14,
-      29,    29,    15,    17,    29,    17,    17,    34,    17,    17,
-      13,    29,    57,    29,    31,    13,    -1,    -1,    -1,    45
+      13,    14,     3,    14,    13,    14,    29,     7,     1,    17,
+       9,    10,    12,    31,    27,    17,    46,    30,    27,    32,
+      13,    30,    43,    32,    42,    17,    17,    44,    44,    17,
+      15,    29,    45,    44,    29,     8,    45,     6,     3,     4,
+       5,    42,     7,    29,    34,    17,    17,    17,    29,    14,
+      17,    29,    29,    17,    29,    17,    15,    17,    63,    17,
+      17,    29,    17,    29,    13,    13,    50,    41,    31,    -1,
+      -1,    32,    -1,    29
 };
 
   /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
      symbol of state STATE-NUM.  */
 static const yytype_int8 yystos[] =
 {
-       0,    47,     0,     3,    42,    50,    63,    17,    64,    51,
-      14,    43,    66,    48,    17,     4,     5,     7,    14,    50,
-      52,    58,    59,    29,    69,     7,    12,    17,    17,    29,
-      68,    65,    17,    29,    15,     8,    67,    65,    14,    44,
-      70,     9,    10,    56,     6,    55,    17,    17,    65,    14,
-      17,    54,    17,    56,    14,    29,    49,    53,    13,    14,
-      27,    30,    32,    45,    50,    52,    57,    59,    60,    61,
-      62,    71,    49,    29,    17,    29,    29,    17,    14,    15,
-      17,    29,    34,    17,    29,    17,    31,    29,    17
+       0,    48,     0,     3,    42,    51,    64,    17,    65,    52,
+      14,    43,    69,    49,    17,     4,     5,     7,    14,    51,
+      53,    59,    60,    29,    67,    72,     7,    12,    17,    17,
+      29,    71,    46,    68,    66,    17,    29,    15,     8,    66,
+      29,    67,    70,    14,    44,    73,     9,    10,    57,     6,
+      56,    17,    17,    14,    68,    66,    17,    55,    17,    57,
+      14,    29,    50,    54,    13,    14,    27,    30,    32,    45,
+      51,    53,    58,    60,    61,    62,    63,    74,    50,    29,
+      17,    29,    29,    17,    14,    15,    17,    29,    34,    17,
+      29,    17,    31,    29,    17
 };
 
   /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
 static const yytype_int8 yyr1[] =
 {
-       0,    46,    47,    47,    47,    48,    48,    48,    48,    48,
-      49,    49,    49,    49,    49,    49,    49,    49,    49,    51,
-      50,    53,    52,    54,    52,    55,    55,    56,    56,    57,
-      58,    59,    60,    60,    61,    62,    62,    62,    63,    64,
-      64,    65,    65,    67,    66,    68,    66,    69,    66,    70,
-      71
+       0,    47,    48,    48,    48,    49,    49,    49,    49,    49,
+      50,    50,    50,    50,    50,    50,    50,    50,    50,    52,
+      51,    54,    53,    55,    53,    56,    56,    57,    57,    58,
+      59,    60,    61,    61,    62,    63,    63,    63,    64,    65,
+      65,    66,    66,    67,    68,    68,    70,    69,    71,    69,
+      72,    69,    73,    74
 };
 
   /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
@@ -726,8 +748,8 @@
        2,     2,     2,     2,     2,     2,     2,     2,     0,     0,
        5,     0,     8,     0,     7,     0,     2,     1,     1,     4,
        4,     4,     3,     4,     4,     5,     4,     3,     3,     2,
-       0,     2,     0,     0,     7,     0,     6,     0,     5,     3,
-       3
+       0,     2,     0,     2,     3,     0,     0,     7,     0,     6,
+       0,     5,     3,     3
 };
 
 
@@ -806,7 +828,7 @@
                        yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
 {
   FILE *yyoutput = yyo;
-  YYUSE (yyoutput);
+  YY_USE (yyoutput);
   if (!yyvaluep)
     return;
 # ifdef YYPRINT
@@ -814,7 +836,7 @@
     YYPRINT (yyo, yytoknum[yykind], *yyvaluep);
 # endif
   YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
-  YYUSE (yykind);
+  YY_USE (yykind);
   YY_IGNORE_MAYBE_UNINITIALIZED_END
 }
 
@@ -928,13 +950,13 @@
 yydestruct (const char *yymsg,
             yysymbol_kind_t yykind, YYSTYPE *yyvaluep)
 {
-  YYUSE (yyvaluep);
+  YY_USE (yyvaluep);
   if (!yymsg)
     yymsg = "Deleting";
   YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
 
   YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
-  YYUSE (yykind);
+  YY_USE (yykind);
   YY_IGNORE_MAYBE_UNINITIALIZED_END
 }
 
@@ -1290,41 +1312,47 @@
                                                               { }
     break;
 
-  case 43: /* $@4: %empty  */
-                                                                                          {
-	cur_field = new_fw_config_field((yyvsp[-2].string), strtoul((yyvsp[-1].string), NULL, 0), strtoul((yyvsp[0].string), NULL, 0));
+  case 43: /* fw_config_field_bits: NUMBER NUMBER  */
+{
+	append_fw_config_bits(&cur_bits, strtoul((yyvsp[-1].string), NULL, 0), strtoul((yyvsp[0].string), NULL, 0));
 }
     break;
 
-  case 44: /* fw_config_field: FW_CONFIG_FIELD STRING NUMBER NUMBER $@4 fw_config_field_children END  */
-                                     { }
+  case 46: /* $@4: %empty  */
+        { cur_field = new_fw_config_field((yyvsp[-2].string), cur_bits); }
     break;
 
-  case 45: /* $@5: %empty  */
+  case 47: /* fw_config_field: FW_CONFIG_FIELD STRING fw_config_field_bits fw_config_field_bits_repeating $@4 fw_config_field_children END  */
+                                     { cur_bits = NULL; }
+    break;
+
+  case 48: /* $@5: %empty  */
                                                             {
-	cur_field = new_fw_config_field((yyvsp[-1].string), strtoul((yyvsp[0].string), NULL, 0), strtoul((yyvsp[0].string), NULL, 0));
+	cur_bits = NULL;
+	append_fw_config_bits(&cur_bits, strtoul((yyvsp[0].string), NULL, 0), strtoul((yyvsp[0].string), NULL, 0));
+	cur_field = new_fw_config_field((yyvsp[-1].string), cur_bits);
 }
     break;
 
-  case 46: /* fw_config_field: FW_CONFIG_FIELD STRING NUMBER $@5 fw_config_field_children END  */
-                                     { }
+  case 49: /* fw_config_field: FW_CONFIG_FIELD STRING NUMBER $@5 fw_config_field_children END  */
+                                     { cur_bits = NULL; }
     break;
 
-  case 47: /* $@6: %empty  */
+  case 50: /* $@6: %empty  */
                                         {
 	cur_field = get_fw_config_field((yyvsp[0].string));
 }
     break;
 
-  case 48: /* fw_config_field: FW_CONFIG_FIELD STRING $@6 fw_config_field_children END  */
-                                     { }
+  case 51: /* fw_config_field: FW_CONFIG_FIELD STRING $@6 fw_config_field_children END  */
+                                     { cur_bits = NULL; }
     break;
 
-  case 49: /* fw_config_option: FW_CONFIG_OPTION STRING NUMBER  */
+  case 52: /* fw_config_option: FW_CONFIG_OPTION STRING NUMBER  */
         { add_fw_config_option(cur_field, (yyvsp[-1].string), strtoull((yyvsp[0].string), NULL, 0)); }
     break;
 
-  case 50: /* fw_config_probe: FW_CONFIG_PROBE STRING STRING  */
+  case 53: /* fw_config_probe: FW_CONFIG_PROBE STRING STRING  */
         { add_fw_config_probe(cur_parent, (yyvsp[-1].string), (yyvsp[0].string)); }
     break;
 
diff --git a/util/sconfig/sconfig.tab.h_shipped b/util/sconfig/sconfig.tab.h_shipped
index 6983c3b..061afc5 100644
--- a/util/sconfig/sconfig.tab.h_shipped
+++ b/util/sconfig/sconfig.tab.h_shipped
@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.7.2.  */
+/* A Bison parser, made by GNU Bison 3.7.5.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
    Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -35,8 +35,8 @@
    especially those whose name start with YY_ or yy_.  They are
    private implementation details that can be changed or removed.  */
 
-#ifndef YY_YY_HOME_C0D3_SRC_COREBOOT_GIT_UTIL_SCONFIG_SCONFIG_TAB_H_SHIPPED_INCLUDED
-# define YY_YY_HOME_C0D3_SRC_COREBOOT_GIT_UTIL_SCONFIG_SCONFIG_TAB_H_SHIPPED_INCLUDED
+#ifndef YY_YY_HOME_TWAWRZYNCZAK_DEVEL_COREBOOT_UTIL_SCONFIG_SCONFIG_TAB_H_SHIPPED_INCLUDED
+# define YY_YY_HOME_TWAWRZYNCZAK_DEVEL_COREBOOT_UTIL_SCONFIG_SCONFIG_TAB_H_SHIPPED_INCLUDED
 /* Debug traces.  */
 #ifndef YYDEBUG
 # define YYDEBUG 0
@@ -96,7 +96,8 @@
     FW_CONFIG_TABLE = 297,         /* FW_CONFIG_TABLE  */
     FW_CONFIG_FIELD = 298,         /* FW_CONFIG_FIELD  */
     FW_CONFIG_OPTION = 299,        /* FW_CONFIG_OPTION  */
-    FW_CONFIG_PROBE = 300          /* FW_CONFIG_PROBE  */
+    FW_CONFIG_PROBE = 300,         /* FW_CONFIG_PROBE  */
+    PIPE = 301                     /* PIPE  */
   };
   typedef enum yytokentype yytoken_kind_t;
 #endif
@@ -123,4 +124,4 @@
 
 int yyparse (void);
 
-#endif /* !YY_YY_HOME_C0D3_SRC_COREBOOT_GIT_UTIL_SCONFIG_SCONFIG_TAB_H_SHIPPED_INCLUDED  */
+#endif /* !YY_YY_HOME_TWAWRZYNCZAK_DEVEL_COREBOOT_UTIL_SCONFIG_SCONFIG_TAB_H_SHIPPED_INCLUDED  */
diff --git a/util/sconfig/sconfig.y b/util/sconfig/sconfig.y
index 8e03791..e60bd16 100755
--- a/util/sconfig/sconfig.y
+++ b/util/sconfig/sconfig.y
@@ -11,6 +11,7 @@
 static struct bus *cur_parent;
 static struct chip_instance *cur_chip_instance;
 static struct fw_config_field *cur_field;
+static struct fw_config_field_bits *cur_bits;
 
 %}
 %union {
@@ -20,7 +21,7 @@
 	uint64_t number;
 }
 
-%token CHIP DEVICE REGISTER ALIAS REFERENCE ASSOCIATION BOOL STATUS MANDATORY BUS RESOURCE END EQUALS HEX STRING PCI PNP I2C APIC CPU_CLUSTER CPU DOMAIN IRQ DRQ SLOT_DESC IO NUMBER SUBSYSTEMID INHERIT IOAPIC_IRQ IOAPIC PCIINT GENERIC SPI USB MMIO LPC ESPI GPIO FW_CONFIG_TABLE FW_CONFIG_FIELD FW_CONFIG_OPTION FW_CONFIG_PROBE
+%token CHIP DEVICE REGISTER ALIAS REFERENCE ASSOCIATION BOOL STATUS MANDATORY BUS RESOURCE END EQUALS HEX STRING PCI PNP I2C APIC CPU_CLUSTER CPU DOMAIN IRQ DRQ SLOT_DESC IO NUMBER SUBSYSTEMID INHERIT IOAPIC_IRQ IOAPIC PCIINT GENERIC SPI USB MMIO LPC ESPI GPIO FW_CONFIG_TABLE FW_CONFIG_FIELD FW_CONFIG_OPTION FW_CONFIG_PROBE PIPE
 %%
 devtree: { cur_parent = root_parent; } | devtree chip | devtree fw_config_table;
 
@@ -97,23 +98,32 @@
 /* field -> option */
 fw_config_field_children: fw_config_field_children fw_config_option | /* empty */ ;
 
-/* field <start-bit> <end-bit> */
-fw_config_field: FW_CONFIG_FIELD STRING NUMBER /* == start bit */ NUMBER /* == end bit */ {
-	cur_field = new_fw_config_field($<string>2, strtoul($<string>3, NULL, 0), strtoul($<string>4, NULL, 0));
-}
-	fw_config_field_children END { };
+/* <start-bit> <end-bit> */
+fw_config_field_bits: NUMBER /* == start bit */ NUMBER /* == end bit */
+{
+	append_fw_config_bits(&cur_bits, strtoul($<string>1, NULL, 0), strtoul($<string>2, NULL, 0));
+};
+
+/* field <start-bit> <end-bit>(| <start-bit> <end-bit>)* */
+fw_config_field_bits_repeating: PIPE fw_config_field_bits fw_config_field_bits_repeating | /* empty */ ;
+
+fw_config_field: FW_CONFIG_FIELD STRING fw_config_field_bits fw_config_field_bits_repeating
+	{ cur_field = new_fw_config_field($<string>2, cur_bits); }
+	fw_config_field_children END { cur_bits = NULL; };
 
 /* field <bit> (for single-bit fields) */
 fw_config_field: FW_CONFIG_FIELD STRING NUMBER /* == bit */ {
-	cur_field = new_fw_config_field($<string>2, strtoul($<string>3, NULL, 0), strtoul($<string>3, NULL, 0));
+	cur_bits = NULL;
+	append_fw_config_bits(&cur_bits, strtoul($<string>3, NULL, 0), strtoul($<string>3, NULL, 0));
+	cur_field = new_fw_config_field($<string>2, cur_bits);
 }
-	fw_config_field_children END { };
+	fw_config_field_children END { cur_bits = NULL; };
 
 /* field (for adding options to an existing field) */
 fw_config_field: FW_CONFIG_FIELD STRING {
 	cur_field = get_fw_config_field($<string>2);
 }
-	fw_config_field_children END { };
+	fw_config_field_children END { cur_bits = NULL; };
 
 /* option <value> */
 fw_config_option: FW_CONFIG_OPTION STRING NUMBER /* == field value */