diff options
Diffstat (limited to 'drivers/gpio/gpio-arizona.c')
| -rw-r--r-- | drivers/gpio/gpio-arizona.c | 144 |
1 files changed, 96 insertions, 48 deletions
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index 0ea853f68db2..a7e98d395d8e 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -1,23 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * gpiolib support for Wolfson Arizona class devices * * Copyright 2012 Wolfson Microelectronics PLC. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ +#include <linux/gpio/driver.h> #include <linux/kernel.h> -#include <linux/slab.h> #include <linux/module.h> -#include <linux/gpio.h> #include <linux/platform_device.h> -#include <linux/seq_file.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> #include <linux/mfd/arizona/core.h> #include <linux/mfd/arizona/pdata.h> @@ -28,31 +23,67 @@ struct arizona_gpio { struct gpio_chip gpio_chip; }; -static inline struct arizona_gpio *to_arizona_gpio(struct gpio_chip *chip) -{ - return container_of(chip, struct arizona_gpio, gpio_chip); -} - static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { - struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip); + struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; + bool persistent = gpiochip_line_is_persistent(chip, offset); + bool change; + int ret; - return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, - ARIZONA_GPN_DIR, ARIZONA_GPN_DIR); + ret = regmap_update_bits_check(arizona->regmap, + ARIZONA_GPIO1_CTRL + offset, + ARIZONA_GPN_DIR, ARIZONA_GPN_DIR, + &change); + if (ret < 0) + return ret; + + if (change && persistent) { + pm_runtime_put_autosuspend(chip->parent); + } + + return 0; } static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) { - struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip); + struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; - unsigned int val; + unsigned int reg, val; int ret; - ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val); + reg = ARIZONA_GPIO1_CTRL + offset; + ret = regmap_read(arizona->regmap, reg, &val); if (ret < 0) return ret; + /* Resume to read actual registers for input pins */ + if (val & ARIZONA_GPN_DIR) { + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) { + dev_err(chip->parent, "Failed to resume: %d\n", ret); + pm_runtime_put_autosuspend(chip->parent); + return ret; + } + + /* Register is cached, drop it to ensure a physical read */ + ret = regcache_drop_region(arizona->regmap, reg, reg); + if (ret < 0) { + dev_err(chip->parent, "Failed to drop cache: %d\n", + ret); + pm_runtime_put_autosuspend(chip->parent); + return ret; + } + + ret = regmap_read(arizona->regmap, reg, &val); + if (ret < 0) { + pm_runtime_put_autosuspend(chip->parent); + return ret; + } + + pm_runtime_put_autosuspend(chip->parent); + } + if (val & ARIZONA_GPN_LVL) return 1; else @@ -62,8 +93,24 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) static int arizona_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value) { - struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip); + struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; + bool persistent = gpiochip_line_is_persistent(chip, offset); + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val); + if (ret < 0) + return ret; + + if ((val & ARIZONA_GPN_DIR) && persistent) { + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) { + dev_err(chip->parent, "Failed to resume: %d\n", ret); + pm_runtime_put(chip->parent); + return ret; + } + } if (value) value = ARIZONA_GPN_LVL; @@ -72,87 +119,88 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip, ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value); } -static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int arizona_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { - struct arizona_gpio *arizona_gpio = to_arizona_gpio(chip); + struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; if (value) value = ARIZONA_GPN_LVL; - regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, - ARIZONA_GPN_LVL, value); + return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, + ARIZONA_GPN_LVL, value); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "arizona", .owner = THIS_MODULE, .direction_input = arizona_gpio_direction_in, .get = arizona_gpio_get, .direction_output = arizona_gpio_direction_out, .set = arizona_gpio_set, - .can_sleep = 1, + .can_sleep = true, }; static int arizona_gpio_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); - struct arizona_pdata *pdata = arizona->dev->platform_data; + struct arizona_pdata *pdata = &arizona->pdata; struct arizona_gpio *arizona_gpio; int ret; + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio), GFP_KERNEL); - if (arizona_gpio == NULL) + if (!arizona_gpio) return -ENOMEM; arizona_gpio->arizona = arizona; arizona_gpio->gpio_chip = template_chip; - arizona_gpio->gpio_chip.dev = &pdev->dev; + arizona_gpio->gpio_chip.parent = &pdev->dev; switch (arizona->type) { case WM5102: case WM5110: + case WM8280: + case WM8997: + case WM8998: + case WM1814: arizona_gpio->gpio_chip.ngpio = 5; break; + case WM1831: + case CS47L24: + arizona_gpio->gpio_chip.ngpio = 2; + break; default: dev_err(&pdev->dev, "Unknown chip variant %d\n", arizona->type); return -EINVAL; } - if (pdata && pdata->gpio_base) + if (pdata->gpio_base) arizona_gpio->gpio_chip.base = pdata->gpio_base; else arizona_gpio->gpio_chip.base = -1; - ret = gpiochip_add(&arizona_gpio->gpio_chip); + pm_runtime_enable(&pdev->dev); + + ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip, + arizona_gpio); if (ret < 0) { + pm_runtime_disable(&pdev->dev); dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, arizona_gpio); - - return ret; - -err: - return ret; -} - -static int arizona_gpio_remove(struct platform_device *pdev) -{ - struct arizona_gpio *arizona_gpio = platform_get_drvdata(pdev); - - return gpiochip_remove(&arizona_gpio->gpio_chip); + return 0; } static struct platform_driver arizona_gpio_driver = { .driver.name = "arizona-gpio", - .driver.owner = THIS_MODULE, .probe = arizona_gpio_probe, - .remove = arizona_gpio_remove, }; module_platform_driver(arizona_gpio_driver); |
