diff options
Diffstat (limited to 'arch/arm/mach-exynos/p10-switch.c')
-rw-r--r-- | arch/arm/mach-exynos/p10-switch.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/p10-switch.c b/arch/arm/mach-exynos/p10-switch.c new file mode 100644 index 0000000..977b6e5 --- /dev/null +++ b/arch/arm/mach-exynos/p10-switch.c @@ -0,0 +1,239 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/semaphore.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> +#include <mach/usb_switch.h> + +struct device *sec_switch_dev; + +enum usb_path_t current_path = USB_PATH_NONE; + +static struct semaphore usb_switch_sem; + +static bool usb_connected; + +static ssize_t show_usb_sel(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const char *mode; + + if (current_path & USB_PATH_CP) { + /* CP */ + mode = "MODEM"; + } else { + /* AP */ + mode = "PDA"; + } + + pr_info("%s: %s\n", __func__, mode); + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t store_usb_sel(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s: %s\n", __func__, buf); + + if (!strncasecmp(buf, "PDA", 3)) { + usb_switch_lock(); + usb_switch_clr_path(USB_PATH_CP); + usb_switch_unlock(); + } else if (!strncasecmp(buf, "MODEM", 5)) { + usb_switch_lock(); + usb_switch_set_path(USB_PATH_CP); + usb_switch_unlock(); + } else { + pr_err("%s: wrong usb_sel value(%s)!!\n", __func__, buf); + return -EINVAL; + } + + return count; +} + +static ssize_t show_uart_sel(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val_sel; + const char *mode; + + val_sel = gpio_get_value(GPIO_UART_SEL); + + if (val_sel == 0) { + /* CP */ + mode = "CP"; + } else { + /* AP */ + mode = "AP"; + } + + pr_info("%s: %s\n", __func__, mode); + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t store_uart_sel(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int uart_sel = -1; + + pr_info("%s: %s\n", __func__, buf); + + if (!strncasecmp(buf, "AP", 2)) { + uart_sel = 1; + } else if (!strncasecmp(buf, "CP", 2)) { + uart_sel = 0; + } else { + pr_err("%s: wrong uart_sel value(%s)!!\n", __func__, buf); + return -EINVAL; + } + + /* 1 for AP, 0 for CP */ + gpio_set_value(GPIO_UART_SEL, uart_sel); + + return count; +} + +static ssize_t show_usb_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const char *state; + + if (usb_connected) + state = "USB_STATE_CONFIGURED"; + else + state = "USB_STATE_NOTCONFIGURED"; + + pr_info("%s: %s\n", __func__, state); + + return sprintf(buf, "%s\n", state); +} + +static DEVICE_ATTR(usb_sel, 0664, show_usb_sel, store_usb_sel); +static DEVICE_ATTR(uart_sel, 0664, show_uart_sel, store_uart_sel); +static DEVICE_ATTR(usb_state, S_IRUGO, show_usb_state, NULL); + +static struct attribute *px_switch_attributes[] = { + &dev_attr_usb_sel.attr, + &dev_attr_uart_sel.attr, + &dev_attr_usb_state.attr, + NULL +}; + +static const struct attribute_group px_switch_group = { + .attrs = px_switch_attributes, +}; + +void set_usb_connection_state(bool connected) +{ + pr_info("%s: set %s\n", __func__, (connected ? "True" : "False")); + + if (usb_connected != connected) { + usb_connected = connected; + + pr_info("%s: send \"usb_state\" sysfs_notify\n", __func__); + sysfs_notify(&sec_switch_dev->kobj, NULL, "usb_state"); + } +} + +static void usb_apply_path(enum usb_path_t path) +{ + pr_info("%s: current gpio before changing : sel1:%d\n", + __func__, gpio_get_value(GPIO_USB_SEL1)); + pr_info("%s: target path %x\n", __func__, path); + + if (path & USB_PATH_ADCCHECK) { + gpio_set_value(GPIO_USB_SEL1, 0); + return; + } + + /* default : AP */ + gpio_set_value(GPIO_USB_SEL1, 1); + return; + +} + +/* + Typical usage of usb switch: + + usb_switch_lock(); (alternatively from hard/soft irq context) + ( or usb_switch_trylock() ) + ... + usb_switch_set_path(USB_PATH_ADCCHECK); + ... + usb_switch_set_path(USB_PATH_TA); + ... + usb_switch_unlock(); (this restores previous usb switch settings) +*/ +void usb_switch_set_path(enum usb_path_t path) +{ + pr_info("%s: %x current_path before changing\n", + __func__, current_path); + + current_path |= path; + usb_apply_path(current_path); +} + +void usb_switch_clr_path(enum usb_path_t path) +{ + pr_info("%s: %x current_path before changing\n", + __func__, current_path); + + current_path &= ~path; + usb_apply_path(current_path); +} + +int usb_switch_lock(void) +{ + return down_interruptible(&usb_switch_sem); +} + +int usb_switch_trylock(void) +{ + return down_trylock(&usb_switch_sem); +} + +void usb_switch_unlock(void) +{ + up(&usb_switch_sem); +} + +static int __init usb_switch_init(void) +{ + int ret; + + gpio_request(GPIO_USB_SEL1, "GPIO_USB_SEL1"); + gpio_request(GPIO_UART_SEL, "GPIO_UART_SEL"); + + gpio_export(GPIO_USB_SEL1, 1); + gpio_export(GPIO_UART_SEL, 1); + + BUG_ON(!sec_class); + sec_switch_dev = device_create(sec_class, NULL, 0, NULL, "switch"); + + BUG_ON(!sec_switch_dev); + gpio_export_link(sec_switch_dev, "GPIO_USB_SEL1", GPIO_USB_SEL1); + gpio_export_link(sec_switch_dev, "GPIO_UART_SEL", GPIO_UART_SEL); + + /*init_MUTEX(&usb_switch_sem);*/ + sema_init(&usb_switch_sem, 1); + + /* create sysfs group */ + ret = sysfs_create_group(&sec_switch_dev->kobj, &px_switch_group); + if (ret) { + pr_err("failed to create px switch attribute group\n"); + return ret; + } + + return 0; +} + +device_initcall(usb_switch_init); |