| /* | |
| * wm8350-core.c -- Device access for Wolfson WM8350 | |
| * | |
| * Copyright 2007, 2008 Wolfson Microelectronics PLC. | |
| * | |
| * Author: Liam Girdwood | |
| * | |
| * 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/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); |