From patchwork Thu Nov 7 16:32:10 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 289424 X-Patchwork-Delegate: trini@ti.com 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 0783F2C0087 for ; Fri, 8 Nov 2013 03:37:03 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 984CF4A3AA; Thu, 7 Nov 2013 17:36:14 +0100 (CET) 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 6jDSG2ZUE+M6; Thu, 7 Nov 2013 17:36:14 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5E8E54A3F9; Thu, 7 Nov 2013 17:34:13 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1BBE44A343 for ; Thu, 7 Nov 2013 17:34:06 +0100 (CET) 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 we8fS0hKxpk5 for ; Thu, 7 Nov 2013 17:34:00 +0100 (CET) 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-ve0-f201.google.com (mail-ve0-f201.google.com [209.85.128.201]) by theia.denx.de (Postfix) with ESMTPS id 370584A3BF for ; Thu, 7 Nov 2013 17:33:29 +0100 (CET) Received: by mail-ve0-f201.google.com with SMTP id jy13so48998veb.2 for ; Thu, 07 Nov 2013 08:33:28 -0800 (PST) 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:mime-version:content-type:content-transfer-encoding; bh=A77HzvB7lFUmiH70CgXPK0p3c9Uz1GPn8b7SFOGL1DI=; b=eQBPRCvIOFm9IamfHLjquTZR8cEWndrCZT1jMgyweVW4lVfODmTbMraKb0hLwUOKqX pM6BdCLsyGE/2hOpTpHiTsEcpoa0tJtruhr2sPf+vKHuwhbNmafbEKA2KCLAWPKtJmiB AXLqkN40wQi749hANV+ntgP45juUUkFMxyV9jmlP03p4XYS6/zyN4C9t1Gsw2SSQl90/ heTvoFu3Blrob/jjrVXVmM9k1cWiNZuzactawXrSLGNuMm9mv8iqTt8OfB2twwylZ6ye yf6/J7OJkqR6/Dkkdr9j0kKTQhv3Bv2gJ/nQ5TfgY7OwTmtE+MCiUlqbusEGEbN7W7Zc 59uw== X-Gm-Message-State: ALoCoQlv8lphNfgktaAu/r+kGmLLVDVGpgpL/TMmng2TvGtogd/wZ0WaubDxmVkYx6xSrSaj4aJxjsuCLN2VlMxwy5/UZxU/3LgpdcKfwhPdVE4pjkf7SgAtvVeL2jBoBIKday0PS/O6Wg0F09qAXoP0cE6AZ/wy+ncUAsnxvDeCNuCrLWFiNb+obLKy6BHZuksRnUh3tAeC X-Received: by 10.236.141.139 with SMTP id g11mr478484yhj.9.1383842008545; Thu, 07 Nov 2013 08:33:28 -0800 (PST) Received: from corp2gmr1-2.hot.corp.google.com (corp2gmr1-2.hot.corp.google.com [172.24.189.93]) by gmr-mx.google.com with ESMTPS id a24si614231yhl.1.2013.11.07.08.33.28 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 07 Nov 2013 08:33:28 -0800 (PST) Received: from kaki.bld.corp.google.com (kaki.bld.corp.google.com [172.29.216.32]) by corp2gmr1-2.hot.corp.google.com (Postfix) with ESMTP id 5C5395A4245; Thu, 7 Nov 2013 08:33:28 -0800 (PST) Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 8EBC6221979; Thu, 7 Nov 2013 09:32:33 -0700 (MST) From: Simon Glass To: U-Boot Mailing List Date: Thu, 7 Nov 2013 09:32:10 -0700 Message-Id: <1383841933-1800-15-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 1.8.4.1 In-Reply-To: <1383841933-1800-1-git-send-email-sjg@chromium.org> References: <1383841933-1800-1-git-send-email-sjg@chromium.org> MIME-Version: 1.0 Cc: Marek Vasut , u-boot-review@google.com, Tom Rini Subject: [U-Boot] [PATCH v6 14/17] dm: Add GPIO support and tests 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: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Add driver model support for GPIOs. Since existing GPIO drivers do not use driver model, this feature must be enabled by CONFIG_DM_GPIO. After all GPO drivers are converted over we can perhaps remove this config. Tests are provided for the sandbox implementation, and are a sufficient sanity check for basic operation. The GPIO uclass understands the concept of named banks of GPIOs, with each GPIO device providing a single bank. Within each bank the GPIOs are numbered using an offset from 0 to n-1. For example a bank named 'b' with 20 offsets will provide GPIOs named b0 to b19. Anonymous GPIO banks are also supported, and are just numbered without any prefix. Each time a GPIO driver is added to the uclass, the GPIOs are renumbered accordinging, so there is always a global GPIO numbering order. Signed-off-by: Simon Glass Signed-off-by: Marek Vasut Signed-off-by: Pavel Herrmann Signed-off-by: Viktor Křivák Signed-off-by: Tomas Hlavacek --- Changes in v6: - Rename platform_data to platdata Changes in v5: - Change to new SPDX license headers Changes in v4: None Changes in v3: - Tidy up comments/documentation in GPIO module - Update GPIO support to use new struct member names Changes in v2: None drivers/gpio/Makefile | 2 + drivers/gpio/gpio-uclass.c | 266 +++++++++++++++++++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 104 ++++++++++++++++++ test/dm/gpio.c | 111 +++++++++++++++++++ 4 files changed, 483 insertions(+) create mode 100644 drivers/gpio/gpio-uclass.c create mode 100644 test/dm/gpio.c diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1165793..eb1b1e7 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,6 +5,8 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_GPIO) += gpio-uclass.o + obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c new file mode 100644 index 0000000..56bfd11 --- /dev/null +++ b/drivers/gpio/gpio-uclass.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/** + * gpio_to_device() - Convert global GPIO number to device, number + * gpio: The numeric representation of the GPIO + * + * Convert the GPIO number to an entry in the list of GPIOs + * or GPIO blocks registered with the GPIO controller. Returns + * entry on success, NULL on error. + */ +static int gpio_to_device(unsigned int gpio, struct device **devp, + unsigned int *offset) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + int ret; + + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + uc_priv = dev->uclass_priv; + if (gpio >= uc_priv->gpio_base && + gpio < uc_priv->gpio_base + uc_priv->gpio_count) { + *devp = dev; + *offset = gpio - uc_priv->gpio_base; + return 0; + } + } + + /* No such GPIO */ + return ret ? ret : -EINVAL; +} + +int gpio_lookup_name(const char *name, struct device **devp, + unsigned int *offsetp, unsigned int *gpiop) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + int ret; + + if (devp) + *devp = NULL; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + ulong offset; + int len; + + uc_priv = dev->uclass_priv; + len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0; + + if (!strncmp(name, uc_priv->bank_name, len)) { + if (strict_strtoul(name + len, 10, &offset)) + continue; + if (devp) + *devp = dev; + if (offsetp) + *offsetp = offset; + if (gpiop) + *gpiop = uc_priv->gpio_base + offset; + return 0; + } + } + + return ret ? ret : -EINVAL; +} + +/** + * gpio_request() - [COMPAT] Request GPIO + * gpio: GPIO number + * label: Name for the requested GPIO + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_request(unsigned gpio, const char *label) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + if (!gpio_get_ops(dev)->request) + return 0; + + return gpio_get_ops(dev)->request(dev, offset, label); +} + +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + if (!gpio_get_ops(dev)->free) + return 0; + return gpio_get_ops(dev)->free(dev, offset); +} + +/** + * gpio_direction_input() - [COMPAT] Set GPIO direction to input + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_input(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->direction_input(dev, offset); +} + +/** + * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->direction_output(dev, offset, value); +} + +/** + * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns the value of the GPIO pin, or negative value + * on error. + */ +int gpio_get_value(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->get_value(dev, offset); +} + +/** + * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin. + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->set_value(dev, offset, value); +} + +const char *gpio_get_bank_info(struct device *dev, int *bit_count) +{ + struct gpio_dev_priv *priv; + + /* Must be called on an active device */ + priv = dev->uclass_priv; + assert(priv); + + *bit_count = priv->gpio_count; + return priv->bank_name; +} + +/* We need to renumber the GPIOs when any driver is probed/removed */ +static int gpio_renumber(void) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + struct uclass *uc; + unsigned base; + int ret; + + ret = uclass_get(UCLASS_GPIO, &uc); + if (ret) + return ret; + + /* Ensure that we have a base for each bank */ + base = 0; + uclass_foreach_dev(dev, uc) { + if (device_active(dev)) { + uc_priv = dev->uclass_priv; + uc_priv->gpio_base = base; + base += uc_priv->gpio_count; + } + } + + return 0; +} + +static int gpio_post_probe(struct device *dev) +{ + return gpio_renumber(); +} + +static int gpio_pre_remove(struct device *dev) +{ + return gpio_renumber(); +} + +UCLASS_DRIVER(gpio) = { + .id = UCLASS_GPIO, + .name = "gpio", + .post_probe = gpio_post_probe, + .pre_remove = gpio_pre_remove, + .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv), +}; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index f541039..e325df4 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -78,4 +78,108 @@ int gpio_get_value(unsigned gpio); * @return 0 if ok, -1 on error */ int gpio_set_value(unsigned gpio, int value); + +/* State of a GPIO, as reported by get_state() */ +enum { + GPIOF_INPUT = 0, + GPIOF_OUTPUT, + GPIOF_UNKNOWN, +}; + +struct device; + +/** + * struct struct dm_gpio_ops - Driver model GPIO operations + * + * Refer to functions above for description. These function largely copy + * the old API. + * + * This is trying to be close to Linux GPIO API. Once the U-Boot uses the + * new DM GPIO API, this should be really easy to flip over to the Linux + * GPIO API-alike interface. + * + * Akso it would be useful to standardise additional functions like + * pullup, slew rate and drive strength. + * + * gpio_request)( and gpio_free() are optional - if NULL then they will + * not be called. + * + * Note that @offset is the offset from the base GPIO of the device. So + * offset 0 is the device's first GPIO and offset o-1 is the last GPIO, + * where o is the number of GPIO lines controlled by the device. A device + * is typically used to control a single bank of GPIOs. Within complex + * SoCs there may be many banks and therefore many devices all referring + * to the different IO addresses within the SoC. + * + * The uclass combines all GPIO devices togther to provide a consistent + * numbering from 0 to n-1, where n is the number of GPIOs in total across + * all devices. Be careful not to confuse offset with gpio in the parameters. + */ +struct dm_gpio_ops { + int (*request)(struct device *dev, unsigned offset, const char *label); + int (*free)(struct device *dev, unsigned offset); + int (*direction_input)(struct device *dev, unsigned offset); + int (*direction_output)(struct device *dev, unsigned offset, + int value); + int (*get_value)(struct device *dev, unsigned offset); + int (*set_value)(struct device *dev, unsigned offset, int value); + int (*get_function)(struct device *dev, unsigned offset); + int (*get_state)(struct device *dev, unsigned offset, char *state, + int maxlen); +}; + +/** + * struct gpio_dev_priv - information about a device used by the uclass + * + * The uclass combines all active GPIO devices into a unified numbering + * scheme. To do this it maintains some private information aobut each + * device. + * + * To implement driver model support in your GPIO driver, add a probe + * handler, and set @gpio_count and @bank_name correctly in that handler. + * This tells the uclass the name of the GPIO bank and the number of GPIOs + * it contains. + * + * @bank_name: Name of the GPIO device (e.g 'a' means GPIOs will be called + * 'A0', 'A1', etc. + * @gpio_count: Number of GPIOs in this device + * @gpio_base: Base GPIO number for this device. For the first active device + * this will be 0; the numbering for others will follow sequentially so that + * @gpio_base for device 1 will equal the number of GPIOs in device 0. + */ +struct gpio_dev_priv { + const char *bank_name; + unsigned gpio_count; + unsigned gpio_base; +}; + +/* Access the GPIO operations for a device */ +#define gpio_get_ops(dev) ((struct dm_gpio_ops *)(dev)->driver->ops) + +/** + * gpio_get_bank_info - Return information about a GPIO bank/device + * + * This looks up a device and returns both its GPIO base name and the number + * of GPIOs it controls. + * + * @dev: Device to look up + * @offset_count: Returns number of GPIOs within this bank + * @return bank name of this device + */ +const char *gpio_get_bank_info(struct device *dev, int *offset_count); + +/** + * gpio_lookup_name - Look up a GPIO name and return its details + * + * This is used to convert a named GPIO into a device, offset and GPIO + * number. + * + * @name: GPIO name to look up + * @devp: Returns pointer to device which contains this GPIO + * @offsetp: Returns the offset number within this device + * @gpiop: Returns the absolute GPIO number, numbered from 0 + */ +int gpio_lookup_name(const char *name, struct device **devp, + unsigned int *offsetp, unsigned int *gpiop); + #endif /* _ASM_GENERIC_GPIO_H_ */ diff --git a/test/dm/gpio.c b/test/dm/gpio.c new file mode 100644 index 0000000..bf632bc --- /dev/null +++ b/test/dm/gpio.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Test that sandbox GPIOs work correctly */ +static int dm_test_gpio(struct dm_test_state *dms) +{ + unsigned int offset, gpio; + struct dm_gpio_ops *ops; + struct device *dev; + const char *name; + int offset_count; + char buf[80]; + + /* + * We expect to get 3 banks. One is anonymous (just numbered) and + * comes from platdata. The other two are named a (20 gpios) + * and b (10 gpios) and come from the device tree. See + * test/dm/test.dts. + */ + ut_assertok(gpio_lookup_name("b4", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "extra-gpios"); + ut_asserteq(4, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 20 + 4, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_str("b", name); + ut_asserteq(10, offset_count); + + /* Get the operations for this device */ + ops = gpio_get_ops(dev); + ut_assert(ops->get_state); + + /* Cannot get a value until it is reserved */ + ut_asserteq(-1, ops->get_value(dev, offset)); + + /* + * Now some tests that use the 'sandbox' back door. All GPIOs + * should default to input, include b4 that we are using here. + */ + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 0 [ ]", buf); + + /* Change it to an output */ + sandbox_gpio_set_direction(dev, offset, 1); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 0 [ ]", buf); + + sandbox_gpio_set_value(dev, offset, 1); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 1 [ ]", buf); + + ut_assertok(ops->request(dev, offset, "testing")); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 1 [x] testing", buf); + + /* Change the value a bit */ + ut_asserteq(1, ops->get_value(dev, offset)); + ut_assertok(ops->set_value(dev, offset, 0)); + ut_asserteq(0, ops->get_value(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 0 [x] testing", buf); + ut_assertok(ops->set_value(dev, offset, 1)); + ut_asserteq(1, ops->get_value(dev, offset)); + + /* Make it an input */ + ut_assertok(ops->direction_input(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 1 [x] testing", buf); + sandbox_gpio_set_value(dev, offset, 0); + ut_asserteq(0, sandbox_gpio_get_value(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 0 [x] testing", buf); + + ut_assertok(ops->free(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 0 [ ]", buf); + + /* Check the 'a' bank also */ + ut_assertok(gpio_lookup_name("a15", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "base-gpios"); + ut_asserteq(15, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 15, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_str("a", name); + ut_asserteq(20, offset_count); + + /* And the anonymous bank */ + ut_assertok(gpio_lookup_name("14", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "gpio_sandbox"); + ut_asserteq(14, offset); + ut_asserteq(14, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_ptr(NULL, name); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT, offset_count); + + return 0; +} +DM_TEST(dm_test_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);