aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/Makefile1
-rw-r--r--arch/arm/mach-omap2/board-tuna-connector.c325
-rwxr-xr-xarch/arm/mach-omap2/board-tuna.c8
-rw-r--r--arch/arm/mach-omap2/board-tuna.h1
4 files changed, 334 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 7f9b7ac..e43e8ec 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -261,6 +261,7 @@ obj-$(CONFIG_MACH_TUNA) += board-tuna-sensors.o
obj-$(CONFIG_MACH_TUNA) += board-tuna-wifi.o
obj-$(CONFIG_MACH_TUNA) += board-tuna-bluetooth.o \
board-tuna-emif.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-connector.o
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \
omap_phy_internal.o \
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..5d70db1
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-connector.c
@@ -0,0 +1,325 @@
+/*
+ * 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/kernel.h>
+#include <linux/platform_data/fsa9480.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/otg.h>
+
+#include <plat/usb.h>
+
+#include "mux.h"
+
+#define GPIO_JACK_INT_N 4
+#define GPIO_MUX3_SEL0 139
+#define GPIO_MUX3_SEL1 140
+#define GPIO_USB_ID_SEL 191
+#define GPIO_IF_UART_SEL 101
+
+#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 USB_ID_SEL_FSA 0
+#define USB_ID_SEL_MHL 1
+
+#define IF_UART_SEL_DEFAULT 1
+
+#define TUNA_OTG_ID_FSA9480_PRIO INT_MIN
+#define TUNA_OTG_ID_FSA9480_LAST_PRIO INT_MAX
+
+struct tuna_otg {
+ struct otg_transceiver otg;
+ struct device dev;
+
+ struct regulator *vusb;
+ bool reg_on;
+};
+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 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 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_mux_usb_to_fsa(bool enable)
+{
+ 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)
+{
+ 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_fsa_usb_detected(int device)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+
+ pr_debug("detected %x\n", device);
+ switch (device) {
+ case FSA9480_DETECT_USB:
+ tuna_vusb_enable(tuna_otg, true);
+
+ 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);
+ break;
+ case FSA9480_DETECT_CHARGER:
+ tuna_mux_usb(TUNA_USB_MUX_FSA);
+ 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_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);
+
+ 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(TUNA_USB_MUX_FSA);
+ tuna_mux_usb_id(TUNA_USB_MUX_FSA);
+
+ 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);
+ break;
+ }
+}
+
+static struct fsa9480_detect_set fsa_detect_sets[] = {
+ {
+ .prio = TUNA_OTG_ID_FSA9480_PRIO,
+ .mask = FSA9480_DETECT_ALL & ~FSA9480_DETECT_USB_HOST,
+ },
+ {
+ .prio = TUNA_OTG_ID_FSA9480_LAST_PRIO,
+ .mask = FSA9480_DETECT_USB_HOST,
+ .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,
+};
+
+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 int tuna_otg_set_vbus(struct otg_transceiver *otg, bool enabled)
+{
+ dev_dbg(otg->dev, "vbus %s\n", enabled ? "on" : "off");
+ 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);
+}
+
+int __init omap4_tuna_connector_init(void)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+ int ret;
+
+ 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);
+ omap_mux_init_gpio(GPIO_JACK_INT_N,
+ OMAP_PIN_INPUT_PULLUP |
+ OMAP_PIN_OFF_INPUT_PULLUP);
+
+ 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;
+ }
+
+ 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));
+
+ return 0;
+}
diff --git a/arch/arm/mach-omap2/board-tuna.c b/arch/arm/mach-omap2/board-tuna.c
index 143d46a..9c09560 100755
--- a/arch/arm/mach-omap2/board-tuna.c
+++ b/arch/arm/mach-omap2/board-tuna.c
@@ -471,16 +471,21 @@ static struct regulator_init_data tuna_vdac = {
},
};
+static struct regulator_consumer_supply tuna_vusb_supply[] = {
+ REGULATOR_SUPPLY("vusb", "tuna_otg"),
+};
+
static struct regulator_init_data tuna_vusb = {
.constraints = {
.min_uV = 3300000,
.max_uV = 3300000,
- .apply_uV = true,
.valid_modes_mask = REGULATOR_MODE_NORMAL
| REGULATOR_MODE_STANDBY,
.valid_ops_mask = REGULATOR_CHANGE_MODE
| REGULATOR_CHANGE_STATUS,
},
+ .num_consumer_supplies = ARRAY_SIZE(tuna_vusb_supply),
+ .consumer_supplies = tuna_vusb_supply,
};
static struct regulator_init_data tuna_clk32kg = {
@@ -859,6 +864,7 @@ static void __init tuna_init(void)
omap4_tuna_power_init();
omap4_tuna_jack_init();
omap4_tuna_sensors_init();
+ omap4_tuna_connector_init();
#ifdef CONFIG_OMAP_HSI_DEVICE
if (TUNA_TYPE_MAGURO == omap4_tuna_get_type())
omap_hsi_init();
diff --git a/arch/arm/mach-omap2/board-tuna.h b/arch/arm/mach-omap2/board-tuna.h
index 933b9e6..5ab7bbd 100644
--- a/arch/arm/mach-omap2/board-tuna.h
+++ b/arch/arm/mach-omap2/board-tuna.h
@@ -31,6 +31,7 @@ void omap4_tuna_jack_init(void);
void omap4_tuna_nfc_init(void);
void omap4_tuna_power_init(void);
void omap4_tuna_sensors_init(void);
+void omap4_tuna_connector_init(void);
int tuna_wlan_init(void);
int omap_hsi_init(void);
void omap4_tuna_emif_init(void);