dm: serial: Add ->getconfig() callback

In some cases it would be good to know the settings, such as parity,
of current serial console. One example might be an ACPI SPCR table
to generate using these parameters.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c
index 4a05ea4..76d26d3 100644
--- a/drivers/serial/sandbox.c
+++ b/drivers/serial/sandbox.c
@@ -163,6 +163,18 @@
 
 #endif /* CONFIG_DEBUG_UART_SANDBOX */
 
+static int sandbox_serial_getconfig(struct udevice *dev, uint *serial_config)
+{
+	uint config = SERIAL_DEFAULT_CONFIG;
+
+	if (!serial_config)
+		return -EINVAL;
+
+	*serial_config = config;
+
+	return 0;
+}
+
 static int sandbox_serial_setconfig(struct udevice *dev, uint serial_config)
 {
 	u8 parity = SERIAL_GET_PARITY(serial_config);
@@ -207,6 +219,7 @@
 	.putc = sandbox_serial_putc,
 	.pending = sandbox_serial_pending,
 	.getc = sandbox_serial_getc,
+	.getconfig = sandbox_serial_getconfig,
 	.setconfig = sandbox_serial_setconfig,
 };
 
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 3ded627..51ae176 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -294,6 +294,20 @@
 		ops->setbrg(gd->cur_serial_dev, gd->baudrate);
 }
 
+int serial_getconfig(uint *config)
+{
+	struct dm_serial_ops *ops;
+
+	if (!gd->cur_serial_dev)
+		return 0;
+
+	ops = serial_get_ops(gd->cur_serial_dev);
+	if (ops->getconfig)
+		return ops->getconfig(gd->cur_serial_dev, config);
+
+	return 0;
+}
+
 int serial_setconfig(uint config)
 {
 	struct dm_serial_ops *ops;
@@ -419,6 +433,8 @@
 		ops->pending += gd->reloc_off;
 	if (ops->clear)
 		ops->clear += gd->reloc_off;
+	if (ops->getconfig)
+		ops->getconfig += gd->reloc_off;
 	if (ops->setconfig)
 		ops->setconfig += gd->reloc_off;
 #if CONFIG_POST & CONFIG_SYS_POST_UART
diff --git a/include/common.h b/include/common.h
index a8e879e..5747836 100644
--- a/include/common.h
+++ b/include/common.h
@@ -364,6 +364,7 @@
 void	serial_puts   (const char *);
 int	serial_getc   (void);
 int	serial_tstc   (void);
+int	serial_getconfig(uint *config);
 int	serial_setconfig(uint config);
 
 /* $(CPU)/speed.c */
diff --git a/include/serial.h b/include/serial.h
index 9133d07..de21514 100644
--- a/include/serial.h
+++ b/include/serial.h
@@ -75,6 +75,8 @@
 
 #define SERIAL_PAR_SHIFT	0
 #define SERIAL_PAR_MASK		(0x03 << SERIAL_PAR_SHIFT)
+#define SERIAL_SET_PARITY(parity) \
+	((parity << SERIAL_PAR_SHIFT) & SERIAL_PAR_MASK)
 #define SERIAL_GET_PARITY(config) \
 	((config & SERIAL_PAR_MASK) >> SERIAL_PAR_SHIFT)
 
@@ -87,6 +89,8 @@
 
 #define SERIAL_BITS_SHIFT	2
 #define SERIAL_BITS_MASK	(0x3 << SERIAL_BITS_SHIFT)
+#define SERIAL_SET_BITS(bits) \
+	((bits << SERIAL_BITS_SHIFT) & SERIAL_BITS_MASK)
 #define SERIAL_GET_BITS(config) \
 	((config & SERIAL_BITS_MASK) >> SERIAL_BITS_SHIFT)
 
@@ -99,6 +103,8 @@
 
 #define SERIAL_STOP_SHIFT	4
 #define SERIAL_STOP_MASK	(0x3 << SERIAL_STOP_SHIFT)
+#define SERIAL_SET_STOP(stop) \
+	((stop << SERIAL_STOP_SHIFT) & SERIAL_STOP_MASK)
 #define SERIAL_GET_STOP(config) \
 	((config & SERIAL_STOP_MASK) >> SERIAL_STOP_SHIFT)
 
@@ -107,9 +113,10 @@
 		      bits << SERIAL_BITS_SHIFT | \
 		      stop << SERIAL_STOP_SHIFT)
 
-#define SERIAL_DEFAULT_CONFIG	SERIAL_PAR_NONE << SERIAL_PAR_SHIFT | \
-				SERIAL_8_BITS << SERIAL_BITS_SHIFT | \
-				SERIAL_ONE_STOP << SERIAL_STOP_SHIFT
+#define SERIAL_DEFAULT_CONFIG \
+			(SERIAL_PAR_NONE << SERIAL_PAR_SHIFT | \
+			 SERIAL_8_BITS << SERIAL_BITS_SHIFT | \
+			 SERIAL_ONE_STOP << SERIAL_STOP_SHIFT)
 
 /**
  * struct struct dm_serial_ops - Driver model serial operations
@@ -189,6 +196,19 @@
 #endif
 
 	/**
+	 * getconfig() - Get the uart configuration
+	 * (parity, 5/6/7/8 bits word length, stop bits)
+	 *
+	 * Get a current config for this device.
+	 *
+	 * @dev: Device pointer
+	 * @parity: parity to use
+	 * @bits: bits number to use
+	 * @stop: stop bits number to use
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*getconfig)(struct udevice *dev, uint *serial_config);
+	/**
 	 * setconfig() - Set up the uart configuration
 	 * (parity, 5/6/7/8 bits word length, stop bits)
 	 *
diff --git a/test/dm/serial.c b/test/dm/serial.c
index 5c603e1..7a1a152 100644
--- a/test/dm/serial.c
+++ b/test/dm/serial.c
@@ -12,6 +12,7 @@
 static int dm_test_serial(struct unit_test_state *uts)
 {
 	struct udevice *dev_serial;
+	uint value_serial;
 
 	ut_assertok(uclass_get_device_by_name(UCLASS_SERIAL, "serial",
 					      &dev_serial));
@@ -22,6 +23,12 @@
 	 * sandbox_serial driver
 	 */
 	ut_assertok(serial_setconfig(SERIAL_DEFAULT_CONFIG));
+	ut_assertok(serial_getconfig(&value_serial));
+	ut_assert(value_serial == SERIAL_DEFAULT_CONFIG);
+	/*
+	 * test with a parameter which is NULL pointer
+	 */
+	ut_asserteq(-EINVAL, serial_getconfig(NULL));
 	/*
 	 * test with a serial config which is not supported by
 	 * sandbox_serial driver: test with wrong parity