blob: a3cb04e8d5dfe77d8b2a70fb01564c13f362b8a8 [file] [log] [blame]
/* 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 <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
#include <linux/gpio.h>
/* switch device header */
#ifdef CONFIG_SWITCH
#include <linux/switch.h>
#endif /* CONFIG_SWITCH */
#include <linux/muic/muic.h>
#include <linux/sec_sysfs.h>
#if defined(CONFIG_MUIC_NOTIFIER)
#include <linux/muic/muic_notifier.h>
#endif /* CONFIG_MUIC_NOTIFIER */
#if defined(CONFIG_MUIC_SUPPORT_CCIC) && defined(CONFIG_CCIC_NOTIFIER)
#include <linux/ccic/ccic_notifier.h>
#endif
#include "muic-internal.h"
struct device *switch_device;
static struct switch_dev switch_dock = {
.name = "dock",
};
struct switch_dev switch_uart3 = {
.name = "uart3", /* sys/class/switch/uart3/state */
};
#if defined(CONFIG_MUIC_NOTIFIER)
static struct notifier_block muic_notifier_block;
void muic_send_dock_intent(int type)
{
pr_info("%s: MUIC dock type(%d)\n", __func__, type);
#ifdef CONFIG_SWITCH
switch_set_state(&switch_dock, type);
#endif
}
static void muic_jig_uart_cb(int jig_state)
{
pr_info("%s(%d)\n", __func__, jig_state);
#ifdef CONFIG_SWITCH
switch_set_state(&switch_uart3, jig_state);
#endif
}
static int muic_dock_attach_notify(int type, const char *name)
{
pr_info("%s: %s\n", __func__, name);
muic_send_dock_intent(type);
return NOTIFY_OK;
}
static int muic_dock_detach_notify(void)
{
pr_info("%s\n", __func__);
muic_send_dock_intent(MUIC_DOCK_DETACHED);
return NOTIFY_OK;
}
static int muic_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
#if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_MUIC_SUPPORT_CCIC)
CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *)data;
muic_attached_dev_t attached_dev = pnoti->cable_type;
#else
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
#endif
bool jig_state = false;
int type = MUIC_DOCK_DETACHED, ret = NOTIFY_DONE;
const char *name;
switch (attached_dev) {
case ATTACHED_DEV_DESKDOCK_MUIC:
case ATTACHED_DEV_DESKDOCK_VB_MUIC:
type = MUIC_DOCK_DESKDOCK;
name = "Desk Dock Attach";
break;
case ATTACHED_DEV_CARDOCK_MUIC:
type = MUIC_DOCK_CARDOCK;
name = "Car Dock Attach";
break;
case ATTACHED_DEV_SMARTDOCK_MUIC:
case ATTACHED_DEV_SMARTDOCK_VB_MUIC:
case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
type = MUIC_DOCK_SMARTDOCK;
name = "Smart Dock Attach";
break;
case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
type = MUIC_DOCK_SMARTDOCK;
name = "Universal MMDock Attach";
break;
case ATTACHED_DEV_AUDIODOCK_MUIC:
type = MUIC_DOCK_AUDIODOCK;
name = "Audio Dock Attach";
break;
case ATTACHED_DEV_HMT_MUIC:
type = MUIC_DOCK_HMT;
name = "HMT Attach";
break;
case ATTACHED_DEV_GAMEPAD_MUIC:
type = MUIC_DOCK_GAMEPAD;
name = "Gamepad Attach";
break;
case ATTACHED_DEV_JIG_UART_ON_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
case ATTACHED_DEV_JIG_USB_ON_MUIC:
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
if (action == MUIC_NOTIFY_CMD_ATTACH)
jig_state = true;
break;
default:
pr_info("%s: ignore(%d)\n", __func__, attached_dev);
break;
}
if (type != MUIC_DOCK_DETACHED) {
if (action == MUIC_NOTIFY_CMD_ATTACH)
ret = muic_dock_attach_notify(type, name);
else if (action == MUIC_NOTIFY_CMD_DETACH)
ret = muic_dock_detach_notify();
}
muic_jig_uart_cb(jig_state);
return ret;
}
#endif /* CONFIG_MUIC_NOTIFIER */
static void muic_init_switch_dev_cb(void)
{
#ifdef CONFIG_SWITCH
int ret;
/* for DockObserver */
ret = switch_dev_register(&switch_dock);
if (ret < 0) {
pr_err("%s: Failed to register dock switch(%d)\n",
__func__, ret);
return;
}
ret = switch_dev_register(&switch_uart3);
if (ret < 0) {
pr_err("%s: Failed to register dock switch(%d)\n",
__func__, ret);
return;
}
#endif /* CONFIG_SWITCH */
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_register(&muic_notifier_block,
muic_handle_notification, MUIC_NOTIFY_DEV_DOCK);
#endif /* CONFIG_MUIC_NOTIFIER */
pr_info("%s: done\n", __func__);
}
static void muic_cleanup_switch_dev_cb(void)
{
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_unregister(&muic_notifier_block);
#endif /* CONFIG_MUIC_NOTIFIER */
pr_info("%s: done\n", __func__);
}
extern struct muic_platform_data muic_pdata;
/* func : set_switch_sel
* switch_sel value get from bootloader comand line
* switch_sel data consist 8 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, &muic_pdata.switch_sel);
muic_pdata.switch_sel = (muic_pdata.switch_sel) & 0xfff;
pr_info("%s: switch_sel: 0x%03x\n", __func__,
muic_pdata.switch_sel);
return muic_pdata.switch_sel;
}
__setup("pmic_info=", set_switch_sel);
int get_switch_sel(void)
{
return muic_pdata.switch_sel;
}
static int set_factory_uart(char *str)
{
int mode;
get_option(&str, &mode);
muic_pdata.is_factory_uart = !!(mode & 0x1);
pr_info("factory_uart: %sable\n",
muic_pdata.is_factory_uart ? "en" : "dis");
return 0;
}
__setup("androidboot.muic_1k=", set_factory_uart);
/* afc_mode:
* 0x31 : Disabled
* 0x30 : Enabled
*/
static int afc_mode = 0;
static int __init set_afc_mode(char *str)
{
int mode;
get_option(&str, &mode);
afc_mode = (mode & 0x0000FF00) >> 8;
pr_info("%s: afc_mode is 0x%02x\n", __func__, afc_mode);
return 0;
}
early_param("charging_mode", set_afc_mode);
int get_afc_mode(void)
{
return afc_mode;
}
bool is_muic_usb_path_ap_usb(void)
{
if (MUIC_PATH_USB_AP == muic_pdata.usb_path) {
pr_info("%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) {
pr_info("%s: [%d]\n", __func__, muic_pdata.usb_path);
return true;
}
return false;
}
static int muic_init_gpio_cb(void *data, int switch_sel)
{
struct muic_platform_data *pdata = (struct muic_platform_data *)data;
const char *usb_mode;
const char *uart_mode;
int ret = 0;
pr_info("%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->uart_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";
}
/* These flags MUST be updated again from probe function */
pdata->rustproof_on = false;
#if !defined(CONFIG_SEC_FACTORY)
if (!(switch_sel & SWITCH_SEL_RUSTPROOF_MASK))
pdata->rustproof_on = true;
#endif /* !CONFIG_SEC_FACTORY */
pdata->afc_disable = false;
if (pdata->set_gpio_uart_sel)
ret = pdata->set_gpio_uart_sel(pdata->uart_path);
pr_info("%s: usb_path(%s), uart_path(%s)\n", __func__,
usb_mode, uart_mode);
switch_device = sec_device_create(NULL, "switch");
if (IS_ERR(switch_device)) {
pr_err("%s Failed to create device(switch)!\n", __func__);
ret = -ENODEV;
}
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,
.jig_uart_cb = muic_jig_uart_cb,
};