/* * Copyright (C) 2011 Samsung, Inc. * Copyright (C) 2011 Google Inc. * * Author: Adam Hampson * Dima Zavin * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }