blob: 6f39c150e015fff8c09913227b3f533f0695fd9e [file] [log] [blame] [edit]
/*
* Copyright (C) 2011 Samsung, Inc.
* Copyright (C) 2011 Google Inc.
*
* Author: Adam Hampson <[email protected]>
* Dima Zavin <[email protected]>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/platform_data/fsa9480.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/otg.h>
#include <linux/delay.h>
#include <linux/sii9234.h>
#include <linux/i2c/twl.h>
#include <linux/mutex.h>
#include <linux/switch.h>
#include <linux/wakelock.h>
#include <plat/usb.h>
#include "mux.h"
#include "board-tuna.h"
#define GPIO_JACK_INT_N 4
#define GPIO_CP_USB_ON 22
#define GPIO_USB_OTG_ID 24
#define GPIO_MHL_SEL 96
#define GPIO_AP_SEL 97
#define GPIO_MUX3_SEL0 139
#define GPIO_MUX3_SEL1 140
#define GPIO_USB_ID_SEL 191
#define GPIO_IF_UART_SEL 101
#define GPIO_MHL_RST 161
#define GPIO_MHL_WAKEUP 64
#define GPIO_MHL_INT 175
#define GPIO_HDMI_EN 100
#define MUX3_SEL0_AP 1
#define MUX3_SEL1_AP 1
#define MUX3_SEL0_MHL 1
#define MUX3_SEL1_MHL 0
#define MUX3_SEL0_FSA 0
#define MUX3_SEL1_FSA 1
#define FSA3200_AP_SEL_AP 0
#define FSA3200_MHL_SEL_AP 0
#define FSA3200_AP_SEL_FSA 1
#define FSA3200_MHL_SEL_FSA 0
#define FSA3200_AP_SEL_MHL 1
#define FSA3200_MHL_SEL_MHL 1
#define USB_ID_SEL_FSA 0
#define USB_ID_SEL_MHL 1
#define IF_UART_SEL_DEFAULT 1
#define IF_UART_SEL_AP 1
#define IF_UART_SEL_CP 0
#define TUNA_MANUAL_USB_NONE 0
#define TUNA_MANUAL_USB_MODEM 1
#define TUNA_MANUAL_USB_AP 2
#define TUNA_MANUAL_UART_NONE 0
#define TUNA_MANUAL_UART_MODEM 1
#define TUNA_MANUAL_UART_LTE 2
#define TUNA_MANUAL_UART_AP 3
#define CHARGERUSB_CTRL1 0x8
#define CHARGERUSB_CTRL3 0xA
#define CHARGERUSB_CINLIMIT 0xE
#define TWL6030_VBUS_IRQ (TWL6030_IRQ_BASE + USB_PRES_INTR_OFFSET)
#define TWL6030_VBUS_FLAGS (IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
#define TWL_REG_CONTROLLER_INT_MASK 0x00
#define TWL_CONTROLLER_MVBUS_DET BIT(1)
#define TWL_CONTROLLER_RSVD BIT(5)
#define TWL_REG_CONTROLLER_STAT1 0x03
#define TWL_STAT1_VBUS_DET BIT(2)
struct tuna_otg {
struct otg_transceiver otg;
struct device dev;
struct regulator *vusb;
struct work_struct set_vbus_work;
struct mutex lock;
bool reg_on;
bool need_vbus_drive;
int usb_manual_mode;
int uart_manual_mode;
int current_device;
struct switch_dev dock_switch;
};
static struct tuna_otg tuna_otg_xceiv;
enum {
TUNA_USB_MUX_FSA = 0,
TUNA_USB_MUX_MHL,
TUNA_USB_MUX_AP,
NUM_TUNA_USB_MUX,
TUNA_USB_MUX_DEFAULT = TUNA_USB_MUX_FSA,
};
static struct {
int mux3_sel0;
int mux3_sel1;
} tuna_usb_mux_states[] = {
[TUNA_USB_MUX_FSA] = { MUX3_SEL0_FSA, MUX3_SEL1_FSA },
[TUNA_USB_MUX_MHL] = { MUX3_SEL0_MHL, MUX3_SEL1_MHL },
[TUNA_USB_MUX_AP] = { MUX3_SEL0_AP, MUX3_SEL1_AP },
};
static struct {
int ap_sel;
int mhl_sel;
} tuna_fsa3200_mux_pair_states[] = {
[TUNA_USB_MUX_FSA] = { FSA3200_AP_SEL_FSA, FSA3200_MHL_SEL_FSA },
[TUNA_USB_MUX_MHL] = { FSA3200_AP_SEL_MHL, FSA3200_MHL_SEL_MHL },
[TUNA_USB_MUX_AP] = { FSA3200_AP_SEL_AP, FSA3200_MHL_SEL_AP },
};
static int tuna_usb_id_mux_states[] = {
[TUNA_USB_MUX_FSA] = USB_ID_SEL_FSA,
[TUNA_USB_MUX_MHL] = USB_ID_SEL_MHL,
[TUNA_USB_MUX_AP] = USB_ID_SEL_FSA,
};
static ssize_t tuna_otg_usb_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf);
static ssize_t tuna_otg_usb_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
static ssize_t tuna_otg_uart_switch_show(struct device *dev,
struct device_attribute *attr,
char *buf);
static ssize_t tuna_otg_uart_switch_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
static DEVICE_ATTR(usb_sel, S_IRUSR | S_IWUSR,
tuna_otg_usb_sel_show, tuna_otg_usb_sel_store);
static DEVICE_ATTR(uart_sel, S_IRUSR | S_IWUSR,
tuna_otg_uart_switch_show, tuna_otg_uart_switch_store);
static struct attribute *manual_mode_attributes[] = {
&dev_attr_usb_sel.attr,
&dev_attr_uart_sel.attr,
NULL,
};
static const struct attribute_group manual_mode_group = {
.attrs = manual_mode_attributes,
};
static bool tuna_twl_chgctrl_init;
static void tuna_mux_usb(int state)
{
BUG_ON(state >= NUM_TUNA_USB_MUX);
pr_debug("mux to %d\n", state);
gpio_direction_output(GPIO_MUX3_SEL0,
tuna_usb_mux_states[state].mux3_sel0);
gpio_direction_output(GPIO_MUX3_SEL1,
tuna_usb_mux_states[state].mux3_sel1);
}
static void tuna_mux_usb_id(int state)
{
BUG_ON(state >= NUM_TUNA_USB_MUX);
pr_debug("mux to %d\n", state);
gpio_direction_output(GPIO_USB_ID_SEL, tuna_usb_id_mux_states[state]);
}
static void tuna_fsa3200_mux_pair(int state)
{
BUG_ON(state >= NUM_TUNA_USB_MUX);
pr_debug("mux to %d\n", state);
gpio_direction_output(GPIO_AP_SEL,
tuna_fsa3200_mux_pair_states[state].ap_sel);
gpio_direction_output(GPIO_MHL_SEL,
tuna_fsa3200_mux_pair_states[state].mhl_sel);
}
static void tuna_mux_usb_to_fsa(bool enable)
{
if (omap4_tuna_get_revision() >= 3) {
tuna_fsa3200_mux_pair(enable ? TUNA_USB_MUX_FSA :
TUNA_USB_MUX_DEFAULT);
} else {
tuna_mux_usb(enable ? TUNA_USB_MUX_FSA : TUNA_USB_MUX_DEFAULT);
/* When switching ID away from FSA, we want to ensure we switch
* it off FSA, and force it to MHL. Ideally, we'd just say mux
* to default, but FSA is likely the default mux position and
* there's no way to force the ID pin to float to the FSA.
*/
tuna_mux_usb_id(enable ? TUNA_USB_MUX_FSA : TUNA_USB_MUX_MHL);
}
}
static void tuna_mux_usb_to_mhl(bool enable)
{
if (omap4_tuna_get_revision() >= 3) {
tuna_fsa3200_mux_pair(enable ? TUNA_USB_MUX_MHL :
TUNA_USB_MUX_DEFAULT);
} else {
tuna_mux_usb(enable ? TUNA_USB_MUX_MHL : TUNA_USB_MUX_DEFAULT);
tuna_mux_usb_id(enable ? TUNA_USB_MUX_MHL : TUNA_USB_MUX_DEFAULT);
}
}
static void tuna_vusb_enable(struct tuna_otg *tuna_otg, bool enable)
{
/* delay getting the regulator until later */
if (IS_ERR_OR_NULL(tuna_otg->vusb)) {
tuna_otg->vusb = regulator_get(&tuna_otg->dev, "vusb");
if (IS_ERR(tuna_otg->vusb)) {
dev_err(&tuna_otg->dev, "cannot get vusb regulator\n");
return;
}
}
if (enable) {
regulator_enable(tuna_otg->vusb);
tuna_otg->reg_on = true;
} else if (tuna_otg->reg_on) {
regulator_disable(tuna_otg->vusb);
tuna_otg->reg_on = false;
}
}
static void tuna_set_vbus_drive(bool enable)
{
if (enable) {
/* Set the VBUS current limit to 500mA */
twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x09,
CHARGERUSB_CINLIMIT);
/* The TWL6030 has a feature to automatically turn on
* boost mode (VBUS Drive) when the ID signal is not
* grounded. This feature needs to be disabled on Tuna
* as the ID signal is not hooked up to the TWL6030.
*/
twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x21,
CHARGERUSB_CTRL3);
twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x40,
CHARGERUSB_CTRL1);
} else {
twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x01,
CHARGERUSB_CTRL3);
twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0, CHARGERUSB_CTRL1);
}
}
static void tuna_ap_usb_attach(struct tuna_otg *tuna_otg)
{
tuna_vusb_enable(tuna_otg, true);
if (omap4_tuna_get_revision() >= 3) {
tuna_fsa3200_mux_pair(TUNA_USB_MUX_AP);
} else {
tuna_mux_usb(TUNA_USB_MUX_AP);
tuna_mux_usb_id(TUNA_USB_MUX_FSA);
}
tuna_otg->otg.state = OTG_STATE_B_IDLE;
tuna_otg->otg.default_a = false;
tuna_otg->otg.last_event = USB_EVENT_VBUS;
atomic_notifier_call_chain(&tuna_otg->otg.notifier,
USB_EVENT_VBUS,
tuna_otg->otg.gadget);
}
static void tuna_ap_usb_detach(struct tuna_otg *tuna_otg)
{
tuna_vusb_enable(tuna_otg, false);
tuna_otg->otg.state = OTG_STATE_B_IDLE;
tuna_otg->otg.default_a = false;
tuna_otg->otg.last_event = USB_EVENT_NONE;
atomic_notifier_call_chain(&tuna_otg->otg.notifier,
USB_EVENT_NONE,
tuna_otg->otg.gadget);
}
static void tuna_cp_usb_attach(struct tuna_otg *tuna_otg)
{
if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO)
gpio_set_value(GPIO_CP_USB_ON, 1);
tuna_mux_usb_to_fsa(true);
}
static void tuna_cp_usb_detach(struct tuna_otg *tuna_otg)
{
if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO)
gpio_set_value(GPIO_CP_USB_ON, 0);
}
static void tuna_usb_host_detach(struct tuna_otg *tuna_otg)
{
/* Make sure the VBUS drive is turned off */
tuna_set_vbus_drive(false);
tuna_vusb_enable(tuna_otg, false);
tuna_otg->otg.state = OTG_STATE_B_IDLE;
tuna_otg->otg.default_a = false;
tuna_otg->otg.last_event = USB_EVENT_NONE;
atomic_notifier_call_chain(&tuna_otg->otg.notifier,
USB_EVENT_NONE,
tuna_otg->otg.gadget);
}
static void tuna_ap_uart_actions(struct tuna_otg *tuna_otg)
{
tuna_mux_usb_to_fsa(true);
gpio_set_value(GPIO_IF_UART_SEL, IF_UART_SEL_AP);
}
static void tuna_cp_uart_actions(struct tuna_otg *tuna_otg)
{
tuna_mux_usb_to_fsa(true);
gpio_set_value(GPIO_IF_UART_SEL, IF_UART_SEL_CP);
}
static void tuna_lte_uart_actions(struct tuna_otg *tuna_otg)
{
tuna_mux_usb_to_fsa(true);
/* The LTE modem's UART lines are connected to the V_AUDIO_L and
* V_AUDIO_R pins on the FSA9480. The RIL will configure the FSA9480
* separately to set manual routing.
*/
}
static void tuna_otg_mask_vbus_irq(void)
{
twl6030_interrupt_mask(TWL6030_CHARGER_CTRL_INT_MASK,
REG_INT_MSK_LINE_C);
twl6030_interrupt_mask(TWL6030_CHARGER_CTRL_INT_MASK,
REG_INT_MSK_STS_C);
}
static void tuna_otg_unmask_vbus_irq(void)
{
if (!tuna_twl_chgctrl_init) {
int r;
r = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
(u8) ~(TWL_CONTROLLER_RSVD |
TWL_CONTROLLER_MVBUS_DET),
TWL_REG_CONTROLLER_INT_MASK);
if (r)
pr_err_once("%s: Error writing twl charge ctrl int mask\n",
__func__);
else
tuna_twl_chgctrl_init = true;
}
twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
REG_INT_MSK_LINE_C);
twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
REG_INT_MSK_STS_C);
}
static bool tuna_otg_vbus_present(void)
{
u8 vbus_state;
twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &vbus_state,
TWL_REG_CONTROLLER_STAT1);
return !!(vbus_state & TWL_STAT1_VBUS_DET);
}
static void tuna_fsa_usb_detected(int device)
{
struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
int old_device;
mutex_lock(&tuna_otg->lock);
old_device = tuna_otg->current_device;
tuna_otg->current_device = device;
pr_debug("detected %x\n", device);
switch (device) {
case FSA9480_DETECT_AV_365K_CHARGER:
tuna_otg_set_dock_switch(1);
/* intentional fall-through */
case FSA9480_DETECT_USB:
if (tuna_otg->usb_manual_mode == TUNA_MANUAL_USB_MODEM)
tuna_cp_usb_attach(tuna_otg);
else
tuna_ap_usb_attach(tuna_otg);
break;
case FSA9480_DETECT_AV_POWERED:
tuna_ap_usb_attach(tuna_otg);
break;
case FSA9480_DETECT_CHARGER:
tuna_mux_usb_to_fsa(true);
tuna_otg->otg.state = OTG_STATE_B_IDLE;
tuna_otg->otg.default_a = false;
tuna_otg->otg.last_event = USB_EVENT_CHARGER;
atomic_notifier_call_chain(&tuna_otg->otg.notifier,
USB_EVENT_CHARGER,
tuna_otg->otg.gadget);
break;
case FSA9480_DETECT_USB_HOST:
tuna_vusb_enable(tuna_otg, true);
if (omap4_tuna_get_revision() >= 3) {
tuna_fsa3200_mux_pair(TUNA_USB_MUX_AP);
} else {
tuna_mux_usb(TUNA_USB_MUX_AP);
tuna_mux_usb_id(TUNA_USB_MUX_FSA);
}
tuna_otg->otg.state = OTG_STATE_A_IDLE;
tuna_otg->otg.default_a = true;
tuna_otg->otg.last_event = USB_EVENT_ID;
atomic_notifier_call_chain(&tuna_otg->otg.notifier,
USB_EVENT_ID,
tuna_otg->otg.gadget);
break;
case FSA9480_DETECT_NONE:
tuna_mux_usb_to_fsa(true);
switch (old_device) {
case FSA9480_DETECT_JIG:
if (tuna_otg->uart_manual_mode == TUNA_MANUAL_UART_NONE)
tuna_ap_uart_actions(tuna_otg);
break;
case FSA9480_DETECT_AV_365K_CHARGER:
tuna_otg_set_dock_switch(0);
/* intentional fall-through */
case FSA9480_DETECT_USB:
if (tuna_otg->usb_manual_mode == TUNA_MANUAL_USB_MODEM)
tuna_cp_usb_detach(tuna_otg);
else
tuna_ap_usb_detach(tuna_otg);
break;
case FSA9480_DETECT_AV_POWERED:
tuna_ap_usb_detach(tuna_otg);
break;
case FSA9480_DETECT_USB_HOST:
tuna_usb_host_detach(tuna_otg);
break;
case FSA9480_DETECT_CHARGER:
tuna_ap_usb_detach(tuna_otg);
break;
case FSA9480_DETECT_AV_365K:
tuna_otg_set_dock_switch(0);
break;
case FSA9480_DETECT_UART:
default:
break;
};
break;
case FSA9480_DETECT_JIG:
switch (tuna_otg->uart_manual_mode) {
case TUNA_MANUAL_UART_AP:
tuna_ap_uart_actions(tuna_otg);
break;
case TUNA_MANUAL_UART_LTE:
tuna_lte_uart_actions(tuna_otg);
break;
case TUNA_MANUAL_UART_MODEM:
default:
tuna_cp_uart_actions(tuna_otg);
break;
};
break;
case FSA9480_DETECT_AV_365K:
tuna_otg_set_dock_switch(1);
break;
case FSA9480_DETECT_UART:
default:
break;
}
mutex_unlock(&tuna_otg->lock);
}
static struct fsa9480_detect_set fsa_detect_sets[] = {
{
.prio = TUNA_OTG_ID_FSA9480_PRIO,
.mask = FSA9480_DETECT_ALL & ~FSA9480_DETECT_AV_POWERED,
},
{
.prio = TUNA_OTG_ID_SII9234_FAILED_PRIO,
.mask = FSA9480_DETECT_AV_POWERED,
},
{
.prio = TUNA_OTG_ID_FSA9480_LAST_PRIO,
.mask = 0,
.fallback = true,
},
};
static struct fsa9480_platform_data tuna_fsa9480_pdata = {
.detect_time = 500,
.detect_sets = fsa_detect_sets,
.num_sets = ARRAY_SIZE(fsa_detect_sets),
.enable = tuna_mux_usb_to_fsa,
.detected = tuna_fsa_usb_detected,
.external_id = GPIO_USB_OTG_ID,
.external_vbus_irq = TWL6030_VBUS_IRQ,
.external_vbus_flags = TWL6030_VBUS_FLAGS,
.mask_vbus_irq = tuna_otg_mask_vbus_irq,
.unmask_vbus_irq = tuna_otg_unmask_vbus_irq,
.vbus_present = tuna_otg_vbus_present,
};
static struct i2c_board_info __initdata tuna_connector_i2c4_boardinfo[] = {
{
I2C_BOARD_INFO("fsa9480", 0x4A >> 1),
.irq = OMAP_GPIO_IRQ(GPIO_JACK_INT_N),
.platform_data = &tuna_fsa9480_pdata,
},
};
static int tuna_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
{
otg->host = host;
if (!host)
otg->state = OTG_STATE_UNDEFINED;
return 0;
}
static int tuna_otg_set_peripheral(struct otg_transceiver *otg,
struct usb_gadget *gadget)
{
otg->gadget = gadget;
if (!gadget)
otg->state = OTG_STATE_UNDEFINED;
return 0;
}
static void tuna_otg_work(struct work_struct *data)
{
struct tuna_otg *tuna_otg = container_of(data, struct tuna_otg,
set_vbus_work);
mutex_lock(&tuna_otg->lock);
/* Only allow VBUS drive when in host mode. */
if (tuna_otg->current_device != FSA9480_DETECT_USB_HOST) {
mutex_unlock(&tuna_otg->lock);
return;
}
tuna_set_vbus_drive(tuna_otg->need_vbus_drive);
mutex_unlock(&tuna_otg->lock);
}
static int tuna_otg_set_vbus(struct otg_transceiver *otg, bool enabled)
{
struct tuna_otg *tuna_otg = container_of(otg, struct tuna_otg, otg);
dev_dbg(otg->dev, "vbus %s\n", enabled ? "on" : "off");
tuna_otg->need_vbus_drive = enabled;
schedule_work(&tuna_otg->set_vbus_work);
return 0;
}
static int tuna_otg_phy_init(struct otg_transceiver *otg)
{
if (otg->last_event == USB_EVENT_ID)
omap4430_phy_power(otg->dev, 1, 1);
else
omap4430_phy_power(otg->dev, 0, 1);
return 0;
}
static void tuna_otg_phy_shutdown(struct otg_transceiver *otg)
{
omap4430_phy_power(otg->dev, 0, 0);
}
static int tuna_otg_set_suspend(struct otg_transceiver *otg, int suspend)
{
return omap4430_phy_suspend(otg->dev, suspend);
}
static ssize_t tuna_otg_usb_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
const char* mode;
switch (tuna_otg->usb_manual_mode) {
case TUNA_MANUAL_USB_AP:
mode = "PDA";
break;
case TUNA_MANUAL_USB_MODEM:
mode = "MODEM";
break;
default:
mode = "NONE";
};
return sprintf(buf, "%s\n", mode);
}
static ssize_t tuna_otg_usb_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
int old_mode;
mutex_lock(&tuna_otg->lock);
old_mode = tuna_otg->usb_manual_mode;
if (!strncasecmp(buf, "PDA", 3)) {
tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_AP;
/* If we are transitioning from CP USB to AP USB then notify the
* USB stack that is now attached.
*/
if (tuna_otg->current_device == FSA9480_DETECT_USB &&
old_mode == TUNA_MANUAL_USB_MODEM) {
tuna_cp_usb_detach(tuna_otg);
tuna_ap_usb_attach(tuna_otg);
}
} else if (!strncasecmp(buf, "MODEM", 5)) {
tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_MODEM;
/* If we are transitioning from AP USB to CP USB then notify the
* USB stack that is has been detached.
*/
if (tuna_otg->current_device == FSA9480_DETECT_USB &&
(old_mode == TUNA_MANUAL_USB_AP ||
old_mode == TUNA_MANUAL_USB_NONE)) {
tuna_ap_usb_detach(tuna_otg);
tuna_cp_usb_attach(tuna_otg);
}
} else if (!strncasecmp(buf, "NONE", 4)) {
tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_NONE;
/* If we are transitioning from CP USB to AP USB then notify the
* USB stack that it is now attached.
*/
if (tuna_otg->current_device == FSA9480_DETECT_USB &&
old_mode == TUNA_MANUAL_USB_MODEM) {
tuna_cp_usb_detach(tuna_otg);
tuna_ap_usb_attach(tuna_otg);
}
}
mutex_unlock(&tuna_otg->lock);
return size;
}
static ssize_t tuna_otg_uart_switch_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
const char* mode;
switch (tuna_otg->uart_manual_mode) {
case TUNA_MANUAL_UART_AP:
mode = "PDA";
break;
case TUNA_MANUAL_UART_MODEM:
mode = "MODEM";
break;
case TUNA_MANUAL_UART_LTE:
mode = "LTEMODEM";
break;
default:
mode = "NONE";
};
return sprintf(buf, "%s\n", mode);
}
static ssize_t tuna_otg_uart_switch_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
mutex_lock(&tuna_otg->lock);
if (!strncasecmp(buf, "PDA", 3)) {
tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_AP;
if (tuna_otg->current_device == FSA9480_DETECT_JIG)
tuna_ap_uart_actions(tuna_otg);
} else if (!strncasecmp(buf, "MODEM", 5)) {
tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_MODEM;
if (tuna_otg->current_device == FSA9480_DETECT_JIG)
tuna_cp_uart_actions(tuna_otg);
} else if (!strncasecmp(buf, "LTEMODEM", 8) &&
omap4_tuna_get_type() == TUNA_TYPE_TORO) {
tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_LTE;
if (tuna_otg->current_device == FSA9480_DETECT_JIG)
tuna_lte_uart_actions(tuna_otg);
} else if (!strncasecmp(buf, "NONE", 4)) {
tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_NONE;
if (tuna_otg->current_device == FSA9480_DETECT_JIG)
tuna_ap_uart_actions(tuna_otg);
}
mutex_unlock(&tuna_otg->lock);
return size;
}
static struct wake_lock sii9234_wake_lock;
#define OMAP_HDMI_HPD_ADDR 0x4A100098
#define OMAP_HDMI_PULLTYPE_MASK 0x00000010
static void sii9234_power(int on)
{
struct omap_mux_partition *p = omap_mux_get("core");
u16 mux;
mux = omap_mux_read(p, OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
if (on) {
gpio_set_value(GPIO_HDMI_EN, 1);
msleep(20);
gpio_set_value(GPIO_MHL_RST, 1);
omap_mux_write(p, mux | OMAP_PULL_UP,
OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
} else {
omap_mux_write(p, mux & ~OMAP_PULL_UP,
OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
gpio_set_value(GPIO_HDMI_EN, 0);
gpio_set_value(GPIO_MHL_RST, 0);
}
}
static void sii9234_enable_vbus(bool enable)
{
}
static void sii9234_connect(bool on, u8 *devcap)
{
struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
unsigned long val;
int dock = 0;
if (on) {
val = USB_EVENT_VBUS;
if (devcap) {
u16 adopter_id =
(devcap[MHL_DEVCAP_ADOPTER_ID_H] << 8) |
devcap[MHL_DEVCAP_ADOPTER_ID_L];
u16 device_id =
(devcap[MHL_DEVCAP_DEVICE_ID_H] << 8) |
devcap[MHL_DEVCAP_DEVICE_ID_L];
if (adopter_id == 0x3333 || adopter_id == 321) {
if (devcap[MHL_DEVCAP_RESERVED] == 2)
val = USB_EVENT_CHARGER;
if (device_id == 0x1234)
dock = 1;
}
}
wake_lock(&sii9234_wake_lock);
} else {
wake_unlock(&sii9234_wake_lock);
val = USB_EVENT_NONE;
}
tuna_otg->otg.state = OTG_STATE_B_IDLE;
tuna_otg->otg.default_a = false;
tuna_otg->otg.last_event = val;
atomic_notifier_call_chain(&tuna_otg->otg.notifier,
val, tuna_otg->otg.gadget);
tuna_otg_set_dock_switch(dock);
}
void tuna_otg_pogo_charger(enum pogo_power_state pogo_state)
{
struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
unsigned long power_state;
switch (pogo_state) {
case POGO_POWER_CHARGER:
power_state = USB_EVENT_CHARGER;
break;
case POGO_POWER_HOST:
power_state = USB_EVENT_VBUS;
break;
case POGO_POWER_DISCONNECTED:
default:
power_state = USB_EVENT_NONE;
break;
}
tuna_otg->otg.state = OTG_STATE_B_IDLE;
tuna_otg->otg.default_a = false;
tuna_otg->otg.last_event = power_state;
atomic_notifier_call_chain(&tuna_otg->otg.notifier, power_state,
tuna_otg->otg.gadget);
}
void tuna_otg_set_dock_switch(int enable)
{
struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
switch_set_state(&tuna_otg->dock_switch, enable);
}
static struct sii9234_platform_data sii9234_pdata = {
.prio = TUNA_OTG_ID_SII9234_PRIO,
.enable = tuna_mux_usb_to_mhl,
.power = sii9234_power,
.enable_vbus = sii9234_enable_vbus,
.connect = sii9234_connect,
};
static struct i2c_board_info __initdata tuna_i2c5_boardinfo[] = {
{
I2C_BOARD_INFO("sii9234_mhl_tx", 0x72>>1),
.irq = OMAP_GPIO_IRQ(GPIO_MHL_INT),
.platform_data = &sii9234_pdata,
},
{
I2C_BOARD_INFO("sii9234_tpi", 0x7A>>1),
.platform_data = &sii9234_pdata,
},
{
I2C_BOARD_INFO("sii9234_hdmi_rx", 0x92>>1),
.platform_data = &sii9234_pdata,
},
{
I2C_BOARD_INFO("sii9234_cbus", 0xC8>>1),
.platform_data = &sii9234_pdata,
},
};
int __init omap4_tuna_connector_init(void)
{
struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
int ret;
if (omap4_tuna_get_revision() >= 3) {
gpio_request(GPIO_MHL_SEL, "fsa3200_mhl_sel");
gpio_request(GPIO_AP_SEL, "fsa3200_ap_sel");
tuna_fsa3200_mux_pair(TUNA_USB_MUX_DEFAULT);
omap_mux_init_gpio(GPIO_MHL_SEL, OMAP_PIN_OUTPUT);
omap_mux_init_gpio(GPIO_AP_SEL, OMAP_PIN_OUTPUT);
} else {
gpio_request(GPIO_MUX3_SEL0, "usb_mux3_sel0");
gpio_request(GPIO_MUX3_SEL1, "usb_mux3_sel1");
gpio_request(GPIO_USB_ID_SEL, "usb_id_sel");
tuna_mux_usb(TUNA_USB_MUX_DEFAULT);
tuna_mux_usb_id(TUNA_USB_MUX_DEFAULT);
omap_mux_init_gpio(GPIO_MUX3_SEL0, OMAP_PIN_OUTPUT);
omap_mux_init_gpio(GPIO_MUX3_SEL1, OMAP_PIN_OUTPUT);
omap_mux_init_gpio(GPIO_USB_ID_SEL, OMAP_PIN_OUTPUT);
}
if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO) {
gpio_request(GPIO_CP_USB_ON, "cp_usb_on");
omap_mux_init_gpio(GPIO_CP_USB_ON, OMAP_PIN_OUTPUT);
gpio_direction_output(GPIO_CP_USB_ON, 0);
}
omap_mux_init_gpio(GPIO_IF_UART_SEL, OMAP_PIN_OUTPUT);
gpio_request(GPIO_IF_UART_SEL, "uart_sel");
gpio_direction_output(GPIO_IF_UART_SEL, IF_UART_SEL_DEFAULT);
omap_mux_init_gpio(GPIO_USB_OTG_ID, OMAP_PIN_INPUT |
OMAP_WAKEUP_EN);
omap_mux_init_gpio(GPIO_JACK_INT_N,
OMAP_PIN_INPUT_PULLUP |
OMAP_PIN_OFF_INPUT_PULLUP);
wake_lock_init(&sii9234_wake_lock, WAKE_LOCK_SUSPEND, "sii9234(mhl)");
mutex_init(&tuna_otg->lock);
INIT_WORK(&tuna_otg->set_vbus_work, tuna_otg_work);
device_initialize(&tuna_otg->dev);
dev_set_name(&tuna_otg->dev, "%s", "tuna_otg");
ret = device_add(&tuna_otg->dev);
if (ret) {
pr_err("%s: cannot reg device '%s' (%d)\n", __func__,
dev_name(&tuna_otg->dev), ret);
return ret;
}
dev_set_drvdata(&tuna_otg->dev, tuna_otg);
tuna_otg->otg.dev = &tuna_otg->dev;
tuna_otg->otg.label = "tuna_otg_xceiv";
tuna_otg->otg.set_host = tuna_otg_set_host;
tuna_otg->otg.set_peripheral = tuna_otg_set_peripheral;
tuna_otg->otg.set_suspend = tuna_otg_set_suspend;
tuna_otg->otg.set_vbus = tuna_otg_set_vbus;
tuna_otg->otg.init = tuna_otg_phy_init;
tuna_otg->otg.shutdown = tuna_otg_phy_shutdown;
ATOMIC_INIT_NOTIFIER_HEAD(&tuna_otg->otg.notifier);
ret = otg_set_transceiver(&tuna_otg->otg);
if (ret)
pr_err("tuna_otg: cannot set transceiver (%d)\n", ret);
omap4430_phy_init(&tuna_otg->dev);
tuna_otg_set_suspend(&tuna_otg->otg, 0);
i2c_register_board_info(4, tuna_connector_i2c4_boardinfo,
ARRAY_SIZE(tuna_connector_i2c4_boardinfo));
ret = sysfs_create_group(&tuna_otg->dev.kobj, &manual_mode_group);
if (ret)
pr_err("tuna_otg: Unable to create manual mode sysfs group"
"(%d)\n", ret);
gpio_request(GPIO_HDMI_EN, NULL);
omap_mux_init_gpio(GPIO_HDMI_EN, OMAP_PIN_OUTPUT);
gpio_direction_output(GPIO_HDMI_EN, 0);
gpio_request(GPIO_MHL_RST, NULL);
omap_mux_init_gpio(GPIO_MHL_RST, OMAP_PIN_OUTPUT);
gpio_direction_output(GPIO_MHL_RST, 0);
gpio_request(GPIO_MHL_INT, NULL);
omap_mux_init_gpio(GPIO_MHL_INT, OMAP_PIN_INPUT);
gpio_direction_input(GPIO_MHL_INT);
gpio_request(TUNA_GPIO_HDMI_HPD, NULL);
omap_mux_init_gpio(TUNA_GPIO_HDMI_HPD, OMAP_PIN_INPUT | OMAP_PULL_ENA);
gpio_direction_input(TUNA_GPIO_HDMI_HPD);
i2c_register_board_info(5, tuna_i2c5_boardinfo,
ARRAY_SIZE(tuna_i2c5_boardinfo));
tuna_otg->dock_switch.name = "dock";
switch_dev_register(&tuna_otg->dock_switch);
return 0;
}