Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * gpiolib support for Wolfson Arizona class devices
0004  *
0005  * Copyright 2012 Wolfson Microelectronics PLC.
0006  *
0007  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/slab.h>
0012 #include <linux/module.h>
0013 #include <linux/gpio/driver.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_runtime.h>
0016 #include <linux/seq_file.h>
0017 
0018 #include <linux/mfd/arizona/core.h>
0019 #include <linux/mfd/arizona/pdata.h>
0020 #include <linux/mfd/arizona/registers.h>
0021 
0022 struct arizona_gpio {
0023     struct arizona *arizona;
0024     struct gpio_chip gpio_chip;
0025 };
0026 
0027 static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
0028 {
0029     struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
0030     struct arizona *arizona = arizona_gpio->arizona;
0031     bool persistent = gpiochip_line_is_persistent(chip, offset);
0032     bool change;
0033     int ret;
0034 
0035     ret = regmap_update_bits_check(arizona->regmap,
0036                        ARIZONA_GPIO1_CTRL + offset,
0037                        ARIZONA_GPN_DIR, ARIZONA_GPN_DIR,
0038                        &change);
0039     if (ret < 0)
0040         return ret;
0041 
0042     if (change && persistent) {
0043         pm_runtime_mark_last_busy(chip->parent);
0044         pm_runtime_put_autosuspend(chip->parent);
0045     }
0046 
0047     return 0;
0048 }
0049 
0050 static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
0051 {
0052     struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
0053     struct arizona *arizona = arizona_gpio->arizona;
0054     unsigned int reg, val;
0055     int ret;
0056 
0057     reg = ARIZONA_GPIO1_CTRL + offset;
0058     ret = regmap_read(arizona->regmap, reg, &val);
0059     if (ret < 0)
0060         return ret;
0061 
0062     /* Resume to read actual registers for input pins */
0063     if (val & ARIZONA_GPN_DIR) {
0064         ret = pm_runtime_get_sync(chip->parent);
0065         if (ret < 0) {
0066             dev_err(chip->parent, "Failed to resume: %d\n", ret);
0067             pm_runtime_put_autosuspend(chip->parent);
0068             return ret;
0069         }
0070 
0071         /* Register is cached, drop it to ensure a physical read */
0072         ret = regcache_drop_region(arizona->regmap, reg, reg);
0073         if (ret < 0) {
0074             dev_err(chip->parent, "Failed to drop cache: %d\n",
0075                 ret);
0076             pm_runtime_put_autosuspend(chip->parent);
0077             return ret;
0078         }
0079 
0080         ret = regmap_read(arizona->regmap, reg, &val);
0081         if (ret < 0) {
0082             pm_runtime_put_autosuspend(chip->parent);
0083             return ret;
0084         }
0085 
0086         pm_runtime_mark_last_busy(chip->parent);
0087         pm_runtime_put_autosuspend(chip->parent);
0088     }
0089 
0090     if (val & ARIZONA_GPN_LVL)
0091         return 1;
0092     else
0093         return 0;
0094 }
0095 
0096 static int arizona_gpio_direction_out(struct gpio_chip *chip,
0097                      unsigned offset, int value)
0098 {
0099     struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
0100     struct arizona *arizona = arizona_gpio->arizona;
0101     bool persistent = gpiochip_line_is_persistent(chip, offset);
0102     unsigned int val;
0103     int ret;
0104 
0105     ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val);
0106     if (ret < 0)
0107         return ret;
0108 
0109     if ((val & ARIZONA_GPN_DIR) && persistent) {
0110         ret = pm_runtime_get_sync(chip->parent);
0111         if (ret < 0) {
0112             dev_err(chip->parent, "Failed to resume: %d\n", ret);
0113             pm_runtime_put(chip->parent);
0114             return ret;
0115         }
0116     }
0117 
0118     if (value)
0119         value = ARIZONA_GPN_LVL;
0120 
0121     return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
0122                   ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
0123 }
0124 
0125 static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
0126 {
0127     struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
0128     struct arizona *arizona = arizona_gpio->arizona;
0129 
0130     if (value)
0131         value = ARIZONA_GPN_LVL;
0132 
0133     regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
0134                ARIZONA_GPN_LVL, value);
0135 }
0136 
0137 static const struct gpio_chip template_chip = {
0138     .label          = "arizona",
0139     .owner          = THIS_MODULE,
0140     .direction_input    = arizona_gpio_direction_in,
0141     .get            = arizona_gpio_get,
0142     .direction_output   = arizona_gpio_direction_out,
0143     .set            = arizona_gpio_set,
0144     .can_sleep      = true,
0145 };
0146 
0147 static int arizona_gpio_probe(struct platform_device *pdev)
0148 {
0149     struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
0150     struct arizona_pdata *pdata = &arizona->pdata;
0151     struct arizona_gpio *arizona_gpio;
0152     int ret;
0153 
0154     device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent));
0155 
0156     arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio),
0157                     GFP_KERNEL);
0158     if (!arizona_gpio)
0159         return -ENOMEM;
0160 
0161     arizona_gpio->arizona = arizona;
0162     arizona_gpio->gpio_chip = template_chip;
0163     arizona_gpio->gpio_chip.parent = &pdev->dev;
0164 
0165     switch (arizona->type) {
0166     case WM5102:
0167     case WM5110:
0168     case WM8280:
0169     case WM8997:
0170     case WM8998:
0171     case WM1814:
0172         arizona_gpio->gpio_chip.ngpio = 5;
0173         break;
0174     case WM1831:
0175     case CS47L24:
0176         arizona_gpio->gpio_chip.ngpio = 2;
0177         break;
0178     default:
0179         dev_err(&pdev->dev, "Unknown chip variant %d\n",
0180             arizona->type);
0181         return -EINVAL;
0182     }
0183 
0184     if (pdata->gpio_base)
0185         arizona_gpio->gpio_chip.base = pdata->gpio_base;
0186     else
0187         arizona_gpio->gpio_chip.base = -1;
0188 
0189     pm_runtime_enable(&pdev->dev);
0190 
0191     ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
0192                      arizona_gpio);
0193     if (ret < 0) {
0194         pm_runtime_disable(&pdev->dev);
0195         dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
0196             ret);
0197         return ret;
0198     }
0199 
0200     return 0;
0201 }
0202 
0203 static struct platform_driver arizona_gpio_driver = {
0204     .driver.name    = "arizona-gpio",
0205     .probe      = arizona_gpio_probe,
0206 };
0207 
0208 module_platform_driver(arizona_gpio_driver);
0209 
0210 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
0211 MODULE_DESCRIPTION("GPIO interface for Arizona devices");
0212 MODULE_LICENSE("GPL");
0213 MODULE_ALIAS("platform:arizona-gpio");