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