| // 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); |