From patchwork Sun Jul 13 18:27:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 369432 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id B20CC1400E7 for ; Mon, 14 Jul 2014 04:30:46 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 46852A7444; Sun, 13 Jul 2014 20:30:05 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id RwggIoYSdkWw; Sun, 13 Jul 2014 20:30:05 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5C852A7464; Sun, 13 Jul 2014 20:29:09 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id A4C76A7416 for ; Sun, 13 Jul 2014 20:28:47 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id X2ZTuLmPFhoN for ; Sun, 13 Jul 2014 20:28:45 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pa0-f73.google.com (mail-pa0-f73.google.com [209.85.220.73]) by theia.denx.de (Postfix) with ESMTPS id 579B8A7419 for ; Sun, 13 Jul 2014 20:28:25 +0200 (CEST) Received: by mail-pa0-f73.google.com with SMTP id kx10so480661pab.2 for ; Sun, 13 Jul 2014 11:28:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=CqLTQ+T5Kd1Yx0xGtlWj44uqAvgmF5lInmOIt8r3QJA=; b=IPABM5B1BhpfTAd7G7NR6yXNz/hPQtbxS/lq21GYSp69UMmzXJBEmCN2yjE2G/AXLk I7Hf/As4xcuoe+lKgPYs85Re5lW7OQs3udeQhagJbM6kKcVPZr8P/doDEJtippYqNz6u r6JdKtfh30685aeY8qpNaIyvr77MfY8fvKAnOsrX+1vPMcETY7e70EmIhS3K+K9y9VbA tkh2d8aORMS0NSHoBDxRbOQWv7mRPz393FBFkEsVCJY/LYoZrBHqysPaymkkczT3I0vM KKuSUe0yVFC3Ko5xE+QJTCDLoUM2DmWEKgIkC9e+UqQ7WiXc7nOikWnoL7f4fi7FJBZm 6leg== X-Gm-Message-State: ALoCoQle5RAY8wg94RF7TmgCO9dxhV25TNHPY/yB2QY1D2UxoGbefeX62oXKgoa3Xci/tUes1bh3 X-Received: by 10.66.228.162 with SMTP id sj2mr5788741pac.11.1405276104194; Sun, 13 Jul 2014 11:28:24 -0700 (PDT) Received: from corp2gmr1-1.hot.corp.google.com (corp2gmr1-1.hot.corp.google.com [172.24.189.92]) by gmr-mx.google.com with ESMTPS id j5si603055yhi.1.2014.07.13.11.28.24 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 13 Jul 2014 11:28:24 -0700 (PDT) Received: from kaki.bld.corp.google.com (kaki.bld.corp.google.com [172.29.216.32]) by corp2gmr1-1.hot.corp.google.com (Postfix) with ESMTP id 00E1A31C32E; Sun, 13 Jul 2014 11:28:24 -0700 (PDT) Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id A0B97221220; Sun, 13 Jul 2014 12:28:23 -0600 (MDT) From: Simon Glass To: U-Boot Mailing List Date: Sun, 13 Jul 2014 12:27:35 -0600 Message-Id: <1405276062-4560-3-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.0.0.526.g5318336 In-Reply-To: <1405276062-4560-1-git-send-email-sjg@chromium.org> References: <1405276062-4560-1-git-send-email-sjg@chromium.org> Cc: Tom Rini Subject: [U-Boot] [PATCH v2 2/9] dm: Add a uclass for serial devices X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 --- Changes in v2: - Rename struct device to struct udevice drivers/serial/Makefile | 4 + drivers/serial/serial-uclass.c | 197 +++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/serial.h | 92 +++++++++++++++++++ 4 files changed, 294 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..c7439f3 --- /dev/null +++ b/drivers/serial/serial-uclass.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* The currently-selected console serial device */ +struct udevice *cur_dev __attribute__ ((section(".data"))); + +#ifndef CONFIG_SYS_MALLOC_F_LEN +#error "Serial is required before relocation - define CONFIG_SYS_MALLOC_F_LEN to make this work" +#endif + +static void serial_find_console_or_panic(void) +{ + /* Check for a console alias */ + if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, + fdtdec_get_alias_node(gd->fdt_blob, "console"), + &cur_dev)) + return; + + /* + * Failing that, get the device with sequence number 0, or in extremis + * just the first serial device we can find. But we insist on having + * a console (even if it is silent). + */ + if (uclass_get_device_by_seq(UCLASS_SERIAL, 0, &cur_dev) && + (uclass_first_device(UCLASS_SERIAL, &cur_dev) || !cur_dev)) + panic("No serial driver found"); +} + +/* Called prior to relocation */ +int serial_init(void) +{ + serial_find_console_or_panic(); + gd->flags |= GD_FLG_SERIAL_READY; + + return 0; +} + +/* Called after relocation */ +void serial_initialize(void) +{ + serial_find_console_or_panic(); +} + +void serial_putc(char ch) +{ + struct dm_serial_ops *ops = serial_get_ops(cur_dev); + int err; + + do { + err = ops->putc(cur_dev, ch); + } while (err == -EAGAIN); + if (ch == '\n') + serial_putc('\r'); +} + +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 udevice *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 udevice *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 udevice *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 udevice *dev) +{ + struct stdio_dev sdev; + struct dm_serial_ops *ops = serial_get_ops(dev); + struct serial_dev_priv *upriv = dev->uclass_priv; + int ret; + + /* Set the baud rate */ + if (ops->setbrg) { + ret = ops->setbrg(dev, gd->baudrate); + if (ret) + return ret; + } + + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + memset(&sdev, '\0', sizeof(sdev)); + + 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, &upriv->sdev); + + return 0; +} + +static int serial_pre_remove(struct udevice *dev) +{ +#ifdef CONFIG_SYS_STDIO_DEREGISTER + struct serial_dev_priv *upriv = dev->uclass_priv; + + if (stdio_deregister_dev(upriv->sdev)) + return -EPERM; +#endif + + 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 dd95fca..7f0e37b 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -21,6 +21,7 @@ enum uclass_id { /* U-Boot uclasses start here */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ + UCLASS_SERIAL, /* Serial UART */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/serial.h b/include/serial.h index d232d47..8f574e4 100644 --- a/include/serial.h +++ b/include/serial.h @@ -72,4 +72,96 @@ extern int write_port(struct stdio_dev *port, char *buf); extern int read_port(struct stdio_dev *port, char *buf, int size); #endif +struct udevice; + +/** + * 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 udevice *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 udevice *dev); + /** + * putc() - Write a character + * + * @dev: Device pointer + * @ch: character to write + * @return 0 if OK, -ve on error + */ + int (*putc)(struct udevice *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 udevice *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 udevice *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 udevice *dev, int on); +#endif +}; + +/** + * struct serial_dev_priv - information about a device used by the uclass + * + * @sdev: stdio device attached to this uart + */ +struct serial_dev_priv { + struct stdio_dev *sdev; +}; + +/* Access the serial operations for a device */ +#define serial_get_ops(dev) ((struct dm_serial_ops *)(dev)->driver->ops) + #endif