aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/board-tuna-connector.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/board-tuna-connector.c')
-rw-r--r--arch/arm/mach-omap2/board-tuna-connector.c982
1 files changed, 982 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/board-tuna-connector.c b/arch/arm/mach-omap2/board-tuna-connector.c
new file mode 100644
index 0000000..7c09aef
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-connector.c
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2011 Samsung, Inc.
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Author: Adam Hampson <ahampson@sta.samsung.com>
+ * Dima Zavin <dima@android.com>
+ *
+ * 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 <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_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_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,
+ },
+ {
+ .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;
+}
+
+#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;
+ }
+ }
+ } else {
+ 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);
+
+ 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;
+}