diff options
Diffstat (limited to 'arch/arm/mach-omap2/board-tuna-connector.c')
-rw-r--r-- | arch/arm/mach-omap2/board-tuna-connector.c | 982 |
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; +} |