ns16550: add support for UART parameters to be specifed with name-value pairs

Add name=value parsing options for com1 and com2 to add flexibility
in setting register values for MMIO UART devices.

Maintain backward compatibility with previous positional parameter
specfications.

eg. com1=115200,8n1,0x3f8,4
eg. com1=115200,8n1,0x3f8,4,reg_width=4,reg_shift=2
eg. com1=baud=115200,parity=n,reg_width=4,reg_shift=2,irq=4

Signed-off-by: Swapnil Paratey <swapnil.paratey@amd.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Swapnil Paratey authored and Jan Beulich committed 2017-06-07 12:35:01 +02:00
1 parent 84c1eab015
commit 97fd49a7e0
3 files changed
+272 -17

No files matched your search

+38
View File
@@ -324,6 +324,44 @@ Both option `com1` and `com2` follow the same format.
A typical setup for most situations might be `com1=115200,8n1`
In addition to the above positional specification for UART parameters,
name=value pair specfications are also supported. This is used to add
flexibility for UART devices which require additional UART parameter
configurations.
The comma separation still delineates positional parameters. Hence,
unless the parameter is explicitly specified with name=value option, it
will be considered a positional parameter.
The syntax consists of
com1=(comma-separated positional parameters),(comma separated name-value pairs)
The accepted name keywords for name=value pairs are
* `baud` - accepts integer baud rate (eg. 115200) or `auto`
* `bridge`- Similar to bridge-bdf in positional parameters.
Used to determine the PCI bridge to access the UART device.
Notation is xx:xx.xx <bus>:<device>.<function>
* `clock-hz`- accepts large integers to setup UART clock frequencies.
Do note - these values are multiplied by 16.
* `data-bits` - integer between 5 and 8
* `dev` - accepted values are `pci` OR `amt`. If this option
is used to specify if the serial device is pci-based. The io_base
cannot be specified when `dev=pci` or `dev=amt` is used.
* `io-base` - accepts integer which specified IO base port for UART registers
* `irq` - IRQ number to use
* `parity` - accepted values are same as positional parameters
* `port` - Used to specify which port the PCI serial device is located on
Notation is xx:xx.xx <bus>:<device>.<function>
* `reg-shift` - register shifts required to set UART registers
* `reg-width` - register width required to set UART registers
(only accepts 1 and 4)
* `stop-bits` - only accepts 1 or 2 for the number of stop bits
The following are examples of correct specifications:
`com1=115200,8n1,0x3f8,4`
`com1=115200,8n1,0x3f8,4,reg_width=4,reg_shift=2`
`com1=baud=115200,parity=n,stop_bits=1,io_base=0x3f8,reg_width=4`
### conring\_size
> `= <size>`
+1 -1
View File
@@ -49,7 +49,7 @@ static void __init assign_integer_param(
static void __init _cmdline_parse(const char *cmdline)
{
char opt[100], *optval, *optkey, *q;
char opt[128], *optval, *optkey, *q;
const char *p = cmdline;
const struct kernel_param *param;
int bool_assert;
+233 -16
View File
@@ -38,11 +38,30 @@
* can be specified in place of a numeric baud rate. Polled mode is specified
* by requesting irq 0.
*/
static char __initdata opt_com1[30] = "";
static char __initdata opt_com2[30] = "";
static char __initdata opt_com1[128] = "";
static char __initdata opt_com2[128] = "";
string_param("com1", opt_com1);
string_param("com2", opt_com2);
enum serial_param_type {
baud,
clock_hz,
data_bits,
io_base,
irq,
parity,
reg_shift,
reg_width,
stop_bits,
#ifdef CONFIG_HAS_PCI
bridge_bdf,
device,
port_bdf,
#endif
/* List all parameters before this line. */
num_serial_params
};
static struct ns16550 {
int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq;
u64 io_base; /* I/O port or memory-mapped I/O address. */
@@ -77,6 +96,32 @@ static struct ns16550 {
#endif
} ns16550_com[2] = { { 0 } };
struct serial_param_var {
char name[12];
enum serial_param_type type;
};
/*
* Enum struct keeping a table of all accepted parameter names for parsing
* com_console_options for serial port com1 and com2.
*/
static const struct serial_param_var __initconst sp_vars[] = {
{"baud", baud},
{"clock-hz", clock_hz},
{"data-bits", data_bits},
{"io-base", io_base},
{"irq", irq},
{"parity", parity},
{"reg-shift", reg_shift},
{"reg-width", reg_width},
{"stop-bits", stop_bits},
#ifdef CONFIG_HAS_PCI
{"bridge", bridge_bdf},
{"dev", device},
{"port", port_bdf},
#endif
};
#ifdef CONFIG_HAS_PCI
struct ns16550_config {
u16 vendor_id;
@@ -1083,26 +1128,73 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx)
}
#endif
/*
* Used to parse name value pairs and return which value it is along with
* pointer for the extracted value.
*/
static enum __init serial_param_type get_token(char *token, char **value)
{
const char *param_name;
unsigned int i;
param_name = strsep(&token, "=");
if ( param_name == NULL )
return num_serial_params;
/* Linear search for the parameter. */
for ( i = 0; i < ARRAY_SIZE(sp_vars); i++ )
{
if ( strcmp(sp_vars[i].name, param_name) == 0 )
{
*value = token;
return sp_vars[i].type;
}
}
return num_serial_params;
}
#define PARSE_ERR(_f, _a...) \
do { \
printk( "ERROR: " _f "\n" , ## _a ); \
return; \
} while ( 0 )
static void __init ns16550_parse_port_config(
struct ns16550 *uart, const char *conf)
#define PARSE_ERR_RET(_f, _a...) \
do { \
printk( "ERROR: " _f "\n" , ## _a ); \
return false; \
} while ( 0 )
static bool __init parse_positional(struct ns16550 *uart, char **str)
{
int baud;
const char *conf;
char *name_val_pos;
/* No user-specified configuration? */
if ( (conf == NULL) || (*conf == '\0') )
conf = *str;
name_val_pos = strchr(conf, '=');
/* Finding the end of the positional parameters. */
while ( name_val_pos > *str )
{
/* Some platforms may automatically probe the UART configuartion. */
if ( uart->baud != 0 )
goto config_parsed;
return;
/* Working backwards from the '=' sign. */
name_val_pos--;
if ( *name_val_pos == ',' )
{
*name_val_pos = '\0';
name_val_pos++;
break;
}
}
*str = name_val_pos;
/* When there are no positional parameters, we return from the function. */
if ( conf == *str )
return true;
/* Parse positional parameters here. */
if ( strncmp(conf, "auto", 4) == 0 )
{
uart->baud = BAUD_AUTO;
@@ -1132,13 +1224,13 @@ static void __init ns16550_parse_port_config(
if ( strncmp(conf, "pci", 3) == 0 )
{
if ( pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com) )
return;
return true;
conf += 3;
}
else if ( strncmp(conf, "amt", 3) == 0 )
{
if ( pci_uart_config(uart, 0, uart - ns16550_com) )
return;
return true;
conf += 3;
}
else
@@ -1157,19 +1249,142 @@ static void __init ns16550_parse_port_config(
conf = parse_pci(conf, NULL, &uart->ps_bdf[0],
&uart->ps_bdf[1], &uart->ps_bdf[2]);
if ( !conf )
PARSE_ERR("Bad port PCI coordinates");
uart->ps_bdf_enable = 1;
PARSE_ERR_RET("Bad port PCI coordinates");
uart->ps_bdf_enable = true;
}
if ( *conf == ',' && *++conf != ',' )
{
if ( !parse_pci(conf, NULL, &uart->pb_bdf[0],
&uart->pb_bdf[1], &uart->pb_bdf[2]) )
PARSE_ERR("Bad bridge PCI coordinates");
uart->pb_bdf_enable = 1;
PARSE_ERR_RET("Bad bridge PCI coordinates");
uart->pb_bdf_enable = true;
}
#endif
return true;
}
static bool __init parse_namevalue_pairs(char *str, struct ns16550 *uart)
{
char *token, *start = str;
char *param_value = NULL;
bool dev_set = false;
if ( (str == NULL) || (*str == '\0') )
return true;
do
{
/* When no tokens are found, start will be NULL */
token = strsep(&start, ",");
switch ( get_token(token, &param_value) )
{
case baud:
uart->baud = simple_strtoul(param_value, NULL, 0);
break;
case clock_hz:
uart->clock_hz = simple_strtoul(param_value, NULL, 0) << 4;
break;
case io_base:
if ( dev_set )
{
printk(XENLOG_WARNING
"Can't use io_base with dev=pci or dev=amt options\n");
break;
}
uart->io_base = simple_strtoul(param_value, NULL, 0);
break;
case irq:
uart->irq = simple_strtoul(param_value, NULL, 0);
break;
case data_bits:
uart->data_bits = simple_strtoul(param_value, NULL, 0);
break;
case parity:
uart->parity = parse_parity_char(*param_value);
break;
case stop_bits:
uart->stop_bits = simple_strtoul(param_value, NULL, 0);
break;
case reg_shift:
uart->reg_shift = simple_strtoul(param_value, NULL, 0);
break;
case reg_width:
uart->reg_width = simple_strtoul(param_value, NULL, 0);
break;
#ifdef CONFIG_HAS_PCI
case bridge_bdf:
if ( !parse_pci(param_value, NULL, &uart->ps_bdf[0],
&uart->ps_bdf[1], &uart->ps_bdf[2]) )
PARSE_ERR_RET("Bad port PCI coordinates\n");
uart->ps_bdf_enable = true;
break;
case device:
if ( strncmp(param_value, "pci", 3) == 0 )
{
pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com);
dev_set = true;
}
else if ( strncmp(param_value, "amt", 3) == 0 )
{
pci_uart_config(uart, 0, uart - ns16550_com);
dev_set = true;
}
break;
case port_bdf:
if ( !parse_pci(param_value, NULL, &uart->pb_bdf[0],
&uart->pb_bdf[1], &uart->pb_bdf[2]) )
PARSE_ERR_RET("Bad port PCI coordinates\n");
uart->pb_bdf_enable = true;
break;
#endif
default:
PARSE_ERR_RET("Invalid parameter: %s\n", token);
}
} while ( start != NULL );
return true;
}
static void __init ns16550_parse_port_config(
struct ns16550 *uart, const char *conf)
{
char com_console_options[128];
char *str;
/* No user-specified configuration? */
if ( (conf == NULL) || (*conf == '\0') )
{
/* Some platforms may automatically probe the UART configuartion. */
if ( uart->baud != 0 )
goto config_parsed;
return;
}
strlcpy(com_console_options, conf, ARRAY_SIZE(com_console_options));
str = com_console_options;
/* parse positional parameters and get pointer for name-value pairs */
if ( !parse_positional(uart, &str) )
return;
if ( !parse_namevalue_pairs(str, uart) )
return;
config_parsed:
/* Sanity checks. */
if ( (uart->baud != BAUD_AUTO) &&
@@ -1177,6 +1392,8 @@ static void __init ns16550_parse_port_config(
PARSE_ERR("Baud rate %d outside supported range.", uart->baud);
if ( (uart->data_bits < 5) || (uart->data_bits > 8) )
PARSE_ERR("%d data bits are unsupported.", uart->data_bits);
if ( (uart->reg_width != 1) && (uart->reg_width != 4) )
PARSE_ERR("Accepted values of reg_width are 1 and 4 only");
if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) )
PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits);
if ( uart->io_base == 0 )
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.