root/drivers/mfd/wm8350-gpio.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * wm8350-core.c  --  Device access for Wolfson WM8350
 *
 * Copyright 2007, 2008 Wolfson Microelectronics PLC.
 *
 * Author: Liam Girdwood
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>

#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/gpio.h>
#include <linux/mfd/wm8350/pmic.h>

static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir)
{
        int ret;

        wm8350_reg_unlock(wm8350);
        if (dir == WM8350_GPIO_DIR_OUT)
                ret = wm8350_clear_bits(wm8350,
                                        WM8350_GPIO_CONFIGURATION_I_O,
                                        1 << gpio);
        else
                ret = wm8350_set_bits(wm8350,
                                      WM8350_GPIO_CONFIGURATION_I_O,
                                      1 << gpio);
        wm8350_reg_lock(wm8350);
        return ret;
}

static int wm8350_gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db)
{
        if (db == WM8350_GPIO_DEBOUNCE_ON)
                return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE,
                                       1 << gpio);
        else
                return wm8350_clear_bits(wm8350,
                                         WM8350_GPIO_DEBOUNCE, 1 << gpio);
}

static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func)
{
        u16 reg;

        wm8350_reg_unlock(wm8350);
        switch (gpio) {
        case 0:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
                    & ~WM8350_GP0_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
                                 reg | ((func & 0xf) << 0));
                break;
        case 1:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
                    & ~WM8350_GP1_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
                                 reg | ((func & 0xf) << 4));
                break;
        case 2:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
                    & ~WM8350_GP2_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
                                 reg | ((func & 0xf) << 8));
                break;
        case 3:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
                    & ~WM8350_GP3_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
                                 reg | ((func & 0xf) << 12));
                break;
        case 4:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
                    & ~WM8350_GP4_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
                                 reg | ((func & 0xf) << 0));
                break;
        case 5:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
                    & ~WM8350_GP5_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
                                 reg | ((func & 0xf) << 4));
                break;
        case 6:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
                    & ~WM8350_GP6_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
                                 reg | ((func & 0xf) << 8));
                break;
        case 7:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
                    & ~WM8350_GP7_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
                                 reg | ((func & 0xf) << 12));
                break;
        case 8:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
                    & ~WM8350_GP8_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
                                 reg | ((func & 0xf) << 0));
                break;
        case 9:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
                    & ~WM8350_GP9_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
                                 reg | ((func & 0xf) << 4));
                break;
        case 10:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
                    & ~WM8350_GP10_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
                                 reg | ((func & 0xf) << 8));
                break;
        case 11:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
                    & ~WM8350_GP11_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
                                 reg | ((func & 0xf) << 12));
                break;
        case 12:
                reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4)
                    & ~WM8350_GP12_FN_MASK;
                wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4,
                                 reg | ((func & 0xf) << 0));
                break;
        default:
                wm8350_reg_lock(wm8350);
                return -EINVAL;
        }

        wm8350_reg_lock(wm8350);
        return 0;
}

static int gpio_set_pull_up(struct wm8350 *wm8350, int gpio, int up)
{
        if (up)
                return wm8350_set_bits(wm8350,
                                       WM8350_GPIO_PIN_PULL_UP_CONTROL,
                                       1 << gpio);
        else
                return wm8350_clear_bits(wm8350,
                                         WM8350_GPIO_PIN_PULL_UP_CONTROL,
                                         1 << gpio);
}

static int gpio_set_pull_down(struct wm8350 *wm8350, int gpio, int down)
{
        if (down)
                return wm8350_set_bits(wm8350,
                                       WM8350_GPIO_PULL_DOWN_CONTROL,
                                       1 << gpio);
        else
                return wm8350_clear_bits(wm8350,
                                         WM8350_GPIO_PULL_DOWN_CONTROL,
                                         1 << gpio);
}

static int gpio_set_polarity(struct wm8350 *wm8350, int gpio, int pol)
{
        if (pol == WM8350_GPIO_ACTIVE_HIGH)
                return wm8350_set_bits(wm8350,
                                       WM8350_GPIO_PIN_POLARITY_TYPE,
                                       1 << gpio);
        else
                return wm8350_clear_bits(wm8350,
                                         WM8350_GPIO_PIN_POLARITY_TYPE,
                                         1 << gpio);
}

static int gpio_set_invert(struct wm8350 *wm8350, int gpio, int invert)
{
        if (invert == WM8350_GPIO_INVERT_ON)
                return wm8350_set_bits(wm8350, WM8350_GPIO_INT_MODE, 1 << gpio);
        else
                return wm8350_clear_bits(wm8350,
                                         WM8350_GPIO_INT_MODE, 1 << gpio);
}

int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
                       int pol, int pull, int invert, int debounce)
{
        /* make sure we never pull up and down at the same time */
        if (pull == WM8350_GPIO_PULL_NONE) {
                if (gpio_set_pull_up(wm8350, gpio, 0))
                        goto err;
                if (gpio_set_pull_down(wm8350, gpio, 0))
                        goto err;
        } else if (pull == WM8350_GPIO_PULL_UP) {
                if (gpio_set_pull_down(wm8350, gpio, 0))
                        goto err;
                if (gpio_set_pull_up(wm8350, gpio, 1))
                        goto err;
        } else if (pull == WM8350_GPIO_PULL_DOWN) {
                if (gpio_set_pull_up(wm8350, gpio, 0))
                        goto err;
                if (gpio_set_pull_down(wm8350, gpio, 1))
                        goto err;
        }

        if (gpio_set_invert(wm8350, gpio, invert))
                goto err;
        if (gpio_set_polarity(wm8350, gpio, pol))
                goto err;
        if (wm8350_gpio_set_debounce(wm8350, gpio, debounce))
                goto err;
        if (gpio_set_dir(wm8350, gpio, dir))
                goto err;
        return gpio_set_func(wm8350, gpio, func);

err:
        return -EIO;
}
EXPORT_SYMBOL_GPL(wm8350_gpio_config);