
Serial devices support simple byte input/output and a few operations to find out whether data is available. Add a basic uclass for serial devices to be used by drivers that are converted to driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/serial/Makefile | 4 + drivers/serial/serial-uclass.c | 161 +++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/serial.h | 90 +++++++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 drivers/serial/serial-uclass.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 571c18f..4720e1d 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -5,7 +5,11 @@ # SPDX-License-Identifier: GPL-2.0+ #
+ifdef CONFIG_DM_SERIAL +obj-y += serial-uclass.o +else obj-y += serial.o +endif
obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c new file mode 100644 index 0000000..22bb1ef --- /dev/null +++ b/drivers/serial/serial-uclass.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2014 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <os.h> +#include <serial.h> +#include <stdio_dev.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* The currently selected console serial device */ +struct device *cur_dev __attribute__ ((section(".data"))); + +/* Called prior to relocation */ +int serial_init(void) +{ + if (uclass_get_preferred_device(UCLASS_SERIAL, &cur_dev)) + panic("No serial driver found"); + gd->flags |= GD_FLG_SERIAL_READY; + + return 0; +} + +/* Called after relocation */ +void serial_initialize(void) +{ + if (uclass_get_preferred_device(UCLASS_SERIAL, &cur_dev)) + panic("No serial driver found"); +} + +void serial_putc(char ch) +{ + struct dm_serial_ops *ops = serial_get_ops(cur_dev); + + ops->putc(cur_dev, ch); +} + +void serial_setbrg(void) +{ + struct dm_serial_ops *ops = serial_get_ops(cur_dev); + + if (ops->setbrg) + ops->setbrg(cur_dev, gd->baudrate); +} + +void serial_puts(const char *str) +{ + while (*str) + serial_putc(*str++); +} + +int serial_tstc(void) +{ + struct dm_serial_ops *ops = serial_get_ops(cur_dev); + + if (ops->pending) + return ops->pending(cur_dev, true); + + return 1; +} + +int serial_getc(void) +{ + struct dm_serial_ops *ops = serial_get_ops(cur_dev); + int err; + + do { + err = ops->getc(cur_dev); + } while (err == -EAGAIN); + + return err >= 0 ? err : 0; +} + +void serial_stdio_init(void) +{ +} + +void serial_stub_putc(struct stdio_dev *sdev, const char ch) +{ + struct device *dev = sdev->priv; + struct dm_serial_ops *ops = serial_get_ops(dev); + + ops->putc(dev, ch); +} + +void serial_stub_puts(struct stdio_dev *sdev, const char *str) +{ + while (*str) + serial_stub_putc(sdev, *str++); +} + +int serial_stub_getc(struct stdio_dev *sdev) +{ + struct device *dev = sdev->priv; + struct dm_serial_ops *ops = serial_get_ops(dev); + + int err; + + do { + err = ops->getc(dev); + } while (err == -EAGAIN); + + return err >= 0 ? err : 0; +} + +int serial_stub_tstc(struct stdio_dev *sdev) +{ + struct device *dev = sdev->priv; + struct dm_serial_ops *ops = serial_get_ops(dev); + + if (ops->pending) + return ops->pending(dev, true); + + return 1; +} + +static int serial_post_probe(struct device *dev) +{ + struct stdio_dev sdev, *new_sdev; + + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + memset(&sdev, 0, sizeof(dev)); + + strncpy(sdev.name, dev->name, sizeof(sdev.name)); + sdev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; + sdev.priv = dev; + sdev.putc = serial_stub_putc; + sdev.puts = serial_stub_puts; + sdev.getc = serial_stub_getc; + sdev.tstc = serial_stub_tstc; + stdio_register_dev(&sdev, &new_sdev); + dev->uclass_priv = new_sdev; + + return 0; +} + +static int serial_pre_remove(struct device *dev) +{ +#ifdef CONFIG_SYS_STDIO_DEREGISTER + if (stdio_deregister_dev(dev->uclass_priv)) + return -EPERM; +#endif + dev->uclass_priv = NULL; + + return 0; +} + +UCLASS_DRIVER(serial) = { + .id = UCLASS_SERIAL, + .name = "serial", + .post_probe = serial_post_probe, + .pre_remove = serial_pre_remove, + .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index f0e691c..e0fb30a 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -20,6 +20,7 @@ enum uclass_id {
/* U-Boot uclasses start here */ UCLASS_GPIO, + UCLASS_SERIAL,
UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/serial.h b/include/serial.h index d232d47..31d1127 100644 --- a/include/serial.h +++ b/include/serial.h @@ -72,4 +72,94 @@ extern int write_port(struct stdio_dev *port, char *buf); extern int read_port(struct stdio_dev *port, char *buf, int size); #endif
+struct device; + +/** + * struct struct dm_serial_ops - Driver model serial operations + * + * The uclass interface is implemented by all serial devices which use + * driver model. + */ +struct dm_serial_ops { + /** + * setbrg() - Set up the baud rate generator + * + * Adjust baud rate divisors to set up a new baud rate for this + * device. Not all devices will support all rates. If the rate + * cannot be supported, the driver is free to select the nearest + * available rate. or return -EINVAL if this is not possible. + * + * @dev: Device pointer + * @baudrate: New baud rate to use + * @return 0 if OK, -ve on error + */ + int (*setbrg)(struct device *dev, int baudrate); + /** + * getc() - Read a character and return it + * + * If no character is available, this should return -EAGAIN without + * waiting. + * + * @dev: Device pointer + * @return character (0..255), -ve on error + */ + int (*getc)(struct device *dev); + /** + * putc() - Write a character + * + * @dev: Device pointer + * @ch: character to write + * @return 0 if OK, -ve on error + */ + int (*putc)(struct device *dev, const char ch); + /** + * pending() - Check if input/output characters are waiting + * + * This can be used to return an indication of the number of waiting + * characters if the driver knows this (e.g. by looking at the FIFO + * level). It is acceptable to return 1 if an indeterminant number + * of characters is waiting. + * + * This method is optional. + * + * @dev: Device pointer + * @input: true to check input characters, false for output + * @return number of waiting characters, 0 for none, -ve on error + */ + int (*pending)(struct device *dev, bool input); + /** + * clear() - Clear the serial FIFOs/holding registers + * + * This method is optional. + * + * This quickly clears any input/output characters from the UART. + * If this is not possible, but characters still exist, then it + * is acceptable to return -EAGAIN (try again) or -EINVAL (not + * supported). + * + * @dev: Device pointer + * @return 0 if OK, -ve on error + */ + int (*clear)(struct device *dev); +#if CONFIG_POST & CONFIG_SYS_POST_UART + /** + * loop() - Control serial device loopback mode + * + * @dev: Device pointer + * @on: 1 to turn loopback on, 0 to turn if off + */ + int (*loop)(struct device *dev, int on); +#endif +}; + +/** + * struct serial_dev_priv - information about a device used by the uclass + */ +struct serial_dev_priv { + int dummy; +}; + +/* Access the serial operations for a device */ +#define serial_get_ops(dev) ((struct dm_serial_ops *)(dev)->driver->ops) + #endif