From patchwork Mon Mar 25 12:06:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Vaittinen, Matti" X-Patchwork-Id: 10868945 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4D950139A for ; Mon, 25 Mar 2019 12:06:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 39A6A2915D for ; Mon, 25 Mar 2019 12:06:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2D32F29283; Mon, 25 Mar 2019 12:06:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 743892915D for ; Mon, 25 Mar 2019 12:06:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731071AbfCYMGg (ORCPT ); Mon, 25 Mar 2019 08:06:36 -0400 Received: from mail-lf1-f66.google.com ([209.85.167.66]:41048 "EHLO mail-lf1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730708AbfCYMGg (ORCPT ); Mon, 25 Mar 2019 08:06:36 -0400 Received: by mail-lf1-f66.google.com with SMTP id 10so5799746lfr.8; Mon, 25 Mar 2019 05:06:33 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=ZQDosu/Tpc94pS4Pkf3MGvQBekPy63DSfRmP96BaSuQ=; b=U4LNGGs3b/lBTOaWqHtNimbRAcKdZe7VM13sCnkRFTqyYjds6NY+sFqAZiwrosbuGQ 7z6D3VE+wBxemsSTCDkXGU52UftCpZp9GdRxeR10u1Oaf1zkjFT40+7XMjF+wrxk7hj0 AOrMa5IHcV/RroXdBLNSLTFYQQj43JKPyqoVYGOI9SWiqLsOseDALrqLZcZShdLpNrx8 MTJbPB1HGZFlpuPjGsjcPNH9zWkiLUrdqefpFZY1P69ATyAS2tdRM2VDes9QlynmvVNL fNWl/kRhsA/c3Ew2HjN+f2B4oG/lGFv0QQrL3e2gOBo/pIVhtAwuPUPmEfoHq4N3c5R9 WV0Q== X-Gm-Message-State: APjAAAVHJTY9uHWD8iUkYA4hyA0Ub9DQTa/RuHgsMV+xI+7RkhWsZMv9 TJhbT6guitilTujb4Jh3tYE= X-Google-Smtp-Source: APXvYqwixbSAfotogpPOr9JlHi7gQyMVK652BCNG3SIxXEyCyu5OFsCnpUmYg533qH3tehk/zEMZwg== X-Received: by 2002:a19:f50f:: with SMTP id j15mr12809801lfb.126.1553515592227; Mon, 25 Mar 2019 05:06:32 -0700 (PDT) Received: from localhost.localdomain ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id w3sm2229701lji.59.2019.03.25.05.06.30 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 25 Mar 2019 05:06:31 -0700 (PDT) Date: Mon, 25 Mar 2019 14:06:19 +0200 From: Matti Vaittinen To: matti.vaittinen@fi.rohmeurope.com, mazziesaccount@gmail.com Cc: Lee Jones , Rob Herring , Mark Rutland , Michael Turquette , Stephen Boyd , Linus Walleij , Bartosz Golaszewski , Sebastian Reichel , Liam Girdwood , Mark Brown , Alessandro Zummo , Alexandre Belloni , Wim Van Sebroeck , Guenter Roeck , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-pm@vger.kernel.org, linux-rtc@vger.kernel.org, linux-watchdog@vger.kernel.org, heikki.haikola@fi.rohmeurope.com, mikko.mutanen@fi.rohmeurope.com Subject: [PATCH v11 5/8] gpio: Initial support for ROHM bd70528 GPIO block Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.10.1 (2018-07-13) Sender: linux-watchdog-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-watchdog@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP ROHM BD70528 PMIC has 4 GPIO pins. Allow them to be controlled by GPIO framework. IRQs are handled by regmap-irq and GPIO driver is not aware of the irq usage. Signed-off-by: Matti Vaittinen Reviewed-by: Linus Walleij --- drivers/gpio/Kconfig | 11 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-bd70528.c | 231 ++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/gpio/gpio-bd70528.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3f50526a771f..2789530a93ae 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -979,6 +979,17 @@ config GPIO_ARIZONA help Support for GPIOs on Wolfson Arizona class devices. +config GPIO_BD70528 + tristate "ROHM BD70528 GPIO support" + depends on MFD_ROHM_BD70528 + help + Support for GPIOs on ROHM BD70528 PMIC. There are four GPIOs + available on the ROHM PMIC in total. The GPIOs can also + generate interrupts. + + This driver can also be built as a module. If so, the module + will be called gpio-bd70528. + config GPIO_BD9571MWV tristate "ROHM BD9571 GPIO support" depends on MFD_BD9571MWV diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 54d55274b93a..e93e376bae0b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o +obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c new file mode 100644 index 000000000000..507703474909 --- /dev/null +++ b/drivers/gpio/gpio-bd70528.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 ROHM Semiconductors +// gpio-bd70528.c ROHM BD70528MWV gpio driver + +#include +#include +#include +#include +#include + +#define GPIO_IN_REG(offset) (BD70528_REG_GPIO1_IN + offset*2) +#define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + offset*2) + +struct bd70528_gpio { + struct rohm_regmap_dev chip; + struct gpio_chip gpio; +}; + +static int bd70528_set_debounce(struct bd70528_gpio *bdgpio, + unsigned int offset, unsigned int debounce) +{ + u8 val; + + switch (debounce) { + case 0: + val = BD70528_DEBOUNCE_DISABLE; + break; + case 1 ... 15: + val = BD70528_DEBOUNCE_15MS; + break; + case 16 ... 30: + val = BD70528_DEBOUNCE_30MS; + break; + case 31 ... 50: + val = BD70528_DEBOUNCE_50MS; + break; + default: + dev_err(bdgpio->chip.dev, + "Invalid debouce value %u\n", debounce); + return -EINVAL; + } + return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset), + BD70528_DEBOUNCE_MASK, val); +} + +static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + int val, ret; + + /* Do we need to do something to IRQs here? */ + ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); + if (ret) { + dev_err(bdgpio->chip.dev, "Could not read gpio direction\n"); + return ret; + } + + return !(val & BD70528_GPIO_OUT_EN_MASK); +} + +static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(bdgpio->chip.regmap, + GPIO_OUT_REG(offset), + BD70528_GPIO_DRIVE_MASK, + BD70528_GPIO_OPEN_DRAIN); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(bdgpio->chip.regmap, + GPIO_OUT_REG(offset), + BD70528_GPIO_DRIVE_MASK, + BD70528_GPIO_PUSH_PULL); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + return bd70528_set_debounce(bdgpio, offset, + pinconf_to_config_argument(config)); + break; + default: + break; + } + return -ENOTSUPP; +} + +static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + /* Do we need to do something to IRQs here? */ + return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + BD70528_GPIO_OUT_EN_MASK, + BD70528_GPIO_OUT_DISABLE); +} +static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + int ret; + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO; + + ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + BD70528_GPIO_OUT_MASK, val); + if (ret) + dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value); +} + +static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + bd70528_gpio_set(chip, offset, value); + return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), + BD70528_GPIO_OUT_EN_MASK, + BD70528_GPIO_OUT_ENABLE); +} + +#define GPIO_IN_STATE_MASK(offset) (BD70528_GPIO_IN_STATE_BASE << offset) + +static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset) +{ + int ret; + unsigned int val; + + ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); + if (!ret) + ret = !!(val & BD70528_GPIO_OUT_MASK); + else + dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n"); + + return ret; +} + +static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset) +{ + unsigned int val; + int ret; + + ret = regmap_read(bdgpio->chip.regmap, BD70528_REG_GPIO_STATE, &val); + + if (!ret) + ret = !(val & GPIO_IN_STATE_MASK(offset)); + else + dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n"); + + return ret; +} + +static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + int ret = -EINVAL; + struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); + + /* + * There is a race condition where someone might be changing the + * GPIO direction after we get it but before we read the value. But + * application design where GPIO direction may be changed just when + * we read GPIO value would be pointless as reader could not know + * whether the returned high/low state is caused by input or output. + * Or then there must be other ways to mitigate the issue. Thus + * locking would make no sense. + */ + ret = bd70528_get_direction(chip, offset); + if (ret == 0) + ret = bd70528_gpio_get_o(bdgpio, offset); + else if (ret == 1) + ret = bd70528_gpio_get_i(bdgpio, offset); + else + dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n"); + + return ret; +} + +static int bd70528_probe(struct platform_device *pdev) +{ + struct bd70528_gpio *bdgpio; + struct rohm_regmap_dev *bd70528; + int ret; + + bd70528 = dev_get_drvdata(pdev->dev.parent); + if (!bd70528) { + dev_err(&pdev->dev, "No MFD driver data\n"); + return -EINVAL; + } + + bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio), + GFP_KERNEL); + if (!bdgpio) + return -ENOMEM; + bdgpio->chip.dev = &pdev->dev; + bdgpio->gpio.parent = pdev->dev.parent; + bdgpio->gpio.label = "bd70528-gpio"; + bdgpio->gpio.owner = THIS_MODULE; + bdgpio->gpio.get_direction = bd70528_get_direction; + bdgpio->gpio.direction_input = bd70528_direction_input; + bdgpio->gpio.direction_output = bd70528_direction_output; + bdgpio->gpio.set_config = bd70528_gpio_set_config; + bdgpio->gpio.can_sleep = true; + bdgpio->gpio.get = bd70528_gpio_get; + bdgpio->gpio.set = bd70528_gpio_set; + bdgpio->gpio.ngpio = 4; + bdgpio->gpio.base = -1; +#ifdef CONFIG_OF_GPIO + bdgpio->gpio.of_node = pdev->dev.parent->of_node; +#endif + bdgpio->chip.regmap = bd70528->regmap; + + ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio, + bdgpio); + if (ret) + dev_err(&pdev->dev, "gpio_init: Failed to add bd70528-gpio\n"); + + return ret; +} + +static struct platform_driver bd70528_gpio = { + .driver = { + .name = "bd70528-gpio" + }, + .probe = bd70528_probe, +}; + +module_platform_driver(bd70528_gpio); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("BD70528 voltage regulator driver"); +MODULE_LICENSE("GPL");