/* drivers/muic/muic-core.c * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include //#include /* switch device header */ #ifdef CONFIG_SWITCH #include #endif /* CONFIG_SWITCH */ #if defined(CONFIG_USE_SAFEOUT) #include #endif #include #if defined(CONFIG_MUIC_NOTIFIER) #include #endif /* CONFIG_MUIC_NOTIFIER */ #ifdef CONFIG_SWITCH static struct switch_dev switch_dock = { .name = "dock", }; #endif /* CONFIG_SWITCH */ /* 1: 619K is used as a wake-up noti which sends a dock noti. * 0: 619K is used 619K itself, JIG_UART_ON */ int muic_wakeup_noti = 1; /* don't access this variable directly!! except get_switch_sel_value function. * you must get switch_sel value by using get_switch_sel function. */ static int switch_sel; /* * func : set_switch_sel * switch_sel value get from bootloader comand line * switch_sel data consist 12 bits (xxxxyyyyzzzz) * first 4bits(zzzz) mean path infomation. * next 4bits(yyyy) mean if pmic version info * next 4bits(xxxx) mean afc disable info */ static int set_switch_sel(char *str) { get_option(&str, &switch_sel); switch_sel = switch_sel & 0x0fff; printk(KERN_DEBUG "[muic] %s : 0x%x\n", __func__, switch_sel); return switch_sel; } __setup("pmic_info=", set_switch_sel); int get_switch_sel(void) { return switch_sel; } #if defined(CONFIG_MUIC_NOTIFIER) static struct notifier_block dock_notifier_block; static void muic_send_dock_intent(int type) { printk(KERN_DEBUG "[muic] %s: MUIC dock type(%d)\n", __func__, type); #ifdef CONFIG_SWITCH switch_set_state(&switch_dock, type); #endif } static int muic_dock_attach_notify(int type, const char *name) { printk(KERN_DEBUG "[muic] %s: %s\n", __func__, name); muic_send_dock_intent(type); return NOTIFY_OK; } static int muic_dock_detach_notify(void) { printk(KERN_DEBUG "[muic] %s\n", __func__); muic_send_dock_intent(MUIC_DOCK_DETACHED); return NOTIFY_OK; } /* Notice: * Define your own wake-up Noti. function to use 619K * as a different purpose as it is for wake-up by default. */ static void __muic_set_wakeup_noti(int flag) { pr_info("%s: %d but 1 by default\n", __func__, flag); muic_wakeup_noti = 1; } void muic_set_wakeup_noti(int flag) __attribute__((weak, alias("__muic_set_wakeup_noti"))); static int muic_handle_dock_notification(struct notifier_block *nb, unsigned long action, void *data) { muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data; int type = MUIC_DOCK_DETACHED; const char *name; if (attached_dev == ATTACHED_DEV_JIG_UART_ON_MUIC) { if (muic_wakeup_noti) { muic_set_wakeup_noti(0); if (action == MUIC_NOTIFY_CMD_ATTACH) { type = MUIC_DOCK_DESKDOCK; name = "Desk Dock Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_DETACH) return muic_dock_detach_notify(); } printk(KERN_DEBUG "[muic] %s: ignore(%d)\n", __func__, attached_dev); return NOTIFY_DONE; } switch (attached_dev) { case ATTACHED_DEV_DESKDOCK_MUIC: case ATTACHED_DEV_DESKDOCK_VB_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) { type = MUIC_DOCK_DESKDOCK; name = "Desk Dock Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_DETACH) return muic_dock_detach_notify(); break; case ATTACHED_DEV_CARDOCK_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) { type = MUIC_DOCK_CARDOCK; name = "Car Dock Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_DETACH) return muic_dock_detach_notify(); break; case ATTACHED_DEV_SMARTDOCK_MUIC: case ATTACHED_DEV_SMARTDOCK_VB_MUIC: case ATTACHED_DEV_SMARTDOCK_TA_MUIC: case ATTACHED_DEV_SMARTDOCK_USB_MUIC: if (action == MUIC_NOTIFY_CMD_LOGICALLY_ATTACH) { type = MUIC_DOCK_SMARTDOCK; name = "Smart Dock Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_LOGICALLY_DETACH) return muic_dock_detach_notify(); break; case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) { type = MUIC_DOCK_SMARTDOCK; name = "Universal MMDock Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_DETACH) return muic_dock_detach_notify(); break; case ATTACHED_DEV_AUDIODOCK_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) { type = MUIC_DOCK_AUDIODOCK; name = "Audio Dock Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_DETACH) return muic_dock_detach_notify(); break; case ATTACHED_DEV_HMT_MUIC: if (action == MUIC_NOTIFY_CMD_ATTACH) { type = MUIC_DOCK_HMT; name = "HMT Attach"; return muic_dock_attach_notify(type, name); } else if (action == MUIC_NOTIFY_CMD_DETACH) return muic_dock_detach_notify(); break; default: break; } printk(KERN_DEBUG "[muic] %s: ignore(%d)\n", __func__, attached_dev); return NOTIFY_DONE; } #endif /* CONFIG_MUIC_NOTIFIER */ #if defined(CONFIG_USE_SAFEOUT) int muic_set_safeout(int safeout_path) { struct regulator *regulator; int ret; printk(KERN_DEBUG "[muic] %s:MUIC safeout path=%d\n", __func__, safeout_path); if (safeout_path == MUIC_PATH_USB_CP) { regulator = regulator_get(NULL, "safeout1_range"); if (IS_ERR(regulator) || regulator == NULL) return -ENODEV; if (regulator_is_enabled(regulator)) regulator_force_disable(regulator); regulator_put(regulator); regulator = regulator_get(NULL, "safeout2_range"); if (IS_ERR(regulator) || regulator == NULL) return -ENODEV; if (!regulator_is_enabled(regulator)) { ret = regulator_enable(regulator); if (ret) goto err; } regulator_put(regulator); } else if (safeout_path == MUIC_PATH_USB_AP) { regulator = regulator_get(NULL, "safeout1_range"); if (IS_ERR(regulator) || regulator == NULL) return -ENODEV; if (!regulator_is_enabled(regulator)) { ret = regulator_enable(regulator); if (ret) goto err; } regulator_put(regulator); regulator = regulator_get(NULL, "safeout2_range"); if (IS_ERR(regulator) || regulator == NULL) return -ENODEV; if (regulator_is_enabled(regulator)) regulator_force_disable(regulator); regulator_put(regulator); } else { printk(KERN_DEBUG "[muic] %s: not control safeout(%d)\n", __func__, safeout_path); return -EINVAL; } return 0; err: printk(KERN_DEBUG "[muic] %s: cannot regulator_enable (%d)\n", __func__, ret); regulator_put(regulator); return ret; } #endif /* CONFIG_USE_SAFEOUT */ static void muic_init_switch_dev_cb(void) { #ifdef CONFIG_SWITCH int ret; /* for DockObserver */ ret = switch_dev_register(&switch_dock); if (ret < 0) { printk(KERN_ERR "[muic] %s: Failed to register dock switch(%d)\n", __func__, ret); return; } #endif /* CONFIG_SWITCH */ #if defined(CONFIG_MUIC_NOTIFIER) muic_notifier_register(&dock_notifier_block, muic_handle_dock_notification, MUIC_NOTIFY_DEV_DOCK); #endif /* CONFIG_MUIC_NOTIFIER */ printk(KERN_DEBUG "[muic] %s: done\n", __func__); } static void muic_cleanup_switch_dev_cb(void) { #if defined(CONFIG_MUIC_NOTIFIER) muic_notifier_unregister(&dock_notifier_block); #endif /* CONFIG_MUIC_NOTIFIER */ printk(KERN_DEBUG "[muic] %s: done\n", __func__); } extern struct muic_platform_data muic_pdata; bool is_muic_usb_path_ap_usb(void) { if (MUIC_PATH_USB_AP == muic_pdata.usb_path) { printk(KERN_DEBUG "[muic] %s: [%d]\n", __func__, muic_pdata.usb_path); return true; } return false; } bool is_muic_usb_path_cp_usb(void) { if (MUIC_PATH_USB_CP == muic_pdata.usb_path) { printk(KERN_DEBUG "[muic] %s: [%d]\n", __func__, muic_pdata.usb_path); return true; } return false; } static int muic_init_gpio_cb(void) { struct muic_platform_data *pdata = &muic_pdata; const char *usb_mode; const char *uart_mode; int ret = 0; printk(KERN_DEBUG "[muic] %s (%d)\n", __func__, switch_sel); if (switch_sel & SWITCH_SEL_USB_MASK) { pdata->usb_path = MUIC_PATH_USB_AP; usb_mode = "PDA"; } else { pdata->usb_path = MUIC_PATH_USB_CP; usb_mode = "MODEM"; } if (pdata->set_gpio_usb_sel) ret = pdata->set_gpio_usb_sel(pdata->usb_path); if (switch_sel & SWITCH_SEL_UART_MASK) { pdata->uart_path = MUIC_PATH_UART_AP; uart_mode = "AP"; } else { pdata->uart_path = MUIC_PATH_UART_CP; uart_mode = "CP"; } if (pdata->set_gpio_uart_sel) ret = pdata->set_gpio_uart_sel(pdata->uart_path); if (!(switch_sel & SWITCH_SEL_RUSTPROOF_MASK)) pdata->rustproof_on = true; else pdata->rustproof_on = false; printk(KERN_DEBUG "[muic] %s: usb_path(%s), uart_path(%s), rustproof(%c)\n", __func__, usb_mode, uart_mode, (pdata->rustproof_on ? 'T' : 'F')); return ret; } struct muic_platform_data muic_pdata = { .init_switch_dev_cb = muic_init_switch_dev_cb, .cleanup_switch_dev_cb = muic_cleanup_switch_dev_cb, .init_gpio_cb = muic_init_gpio_cb, #if defined(CONFIG_USE_SAFEOUT) .set_safeout = muic_set_safeout, #endif /* CONFIG_USE_SAFEOUT */ };