aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Hampson <ahampson@sta.samsung.com>2011-08-17 10:33:42 -0700
committerSimon Wilson <simonwilson@google.com>2011-10-09 13:40:44 -0700
commit2cfe35654a2e72bbd0da04647f0acd9bb8b13323 (patch)
tree26c64e760f9e2524d0c54dfe043565835e323220
parentffc2b67857d88246b8ba1f8fdf7cb1c9afa63bd5 (diff)
downloadkernel_samsung_tuna-2cfe35654a2e72bbd0da04647f0acd9bb8b13323.zip
kernel_samsung_tuna-2cfe35654a2e72bbd0da04647f0acd9bb8b13323.tar.gz
kernel_samsung_tuna-2cfe35654a2e72bbd0da04647f0acd9bb8b13323.tar.bz2
ARM: omap4: tuna: Add support for POGO detection
Change-Id: Iaf0559b73f8a5c6f943350a8dc8350a40e63ab51 Signed-off-by: Adam Hampson <ahampson@sta.samsung.com> Signed-off-by: Rom Lemarchand <rlemarchand@sta.samsung.com>
-rw-r--r--arch/arm/mach-omap2/Makefile1
-rw-r--r--arch/arm/mach-omap2/board-tuna-pogo.c282
-rw-r--r--arch/arm/mach-omap2/board-tuna.c4
-rw-r--r--arch/arm/mach-omap2/board-tuna.h1
4 files changed, 285 insertions, 3 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 418a592..961a34f 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -267,6 +267,7 @@ 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_TUNA) += board-tuna-pogo.o
obj-$(CONFIG_MACH_TUNA) += board-tuna-usbhost.o
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \
diff --git a/arch/arm/mach-omap2/board-tuna-pogo.c b/arch/arm/mach-omap2/board-tuna-pogo.c
new file mode 100644
index 0000000..b235a33
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-pogo.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2011 Samsung, Inc.
+ *
+ * Author: Adam Hampson <ahampson@sta.samsung.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/kernel.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/wakelock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+
+#include <asm/div64.h>
+
+#include "mux.h"
+#include "control.h"
+#include "board-tuna.h"
+
+#define GPIO_POGO_DATA 121
+#define GPIO_POGO_DET 169
+
+/* The below constants are in milliseconds */
+#define POGO_WAKE_PERIOD 100
+#define POGO_ID_PERIOD_TIMEOUT 750
+#define POGO_ID_CARDOCK 100
+#define POGO_ID_DESKDOCK 200
+#define POGO_ENTER_SPDIF_PERIOD 100
+#define POGO_ENTER_SPDIF_WAIT_PERIOD 100
+#define POGO_ID_PERIOD_TOLERANCE 20
+
+#define POGO_DOCK_ID_MAX_RETRY 10
+
+#define POGO_AUDIO_DISCONNECTED 0
+#define POGO_AUDIO_CONNECTED 2
+
+#define POGO_DOCK_UNDOCKED 0
+#define POGO_DOCK_DESK 1
+#define POGO_DOCK_CAR 2
+
+
+struct tuna_pogo {
+ struct switch_dev dock_switch;
+ struct switch_dev audio_switch;
+ struct wake_lock wake_lock;
+ struct completion completion;
+ struct timespec rise_time;
+ struct timespec fall_time;
+ int det_irq;
+ int data_irq;
+ int dock_type;
+ bool fall_detected;
+};
+static struct tuna_pogo tuna_pogo;
+
+static void pogo_send_pulse(unsigned int duration_in_ms)
+{
+ gpio_direction_output(GPIO_POGO_DATA, 1);
+ msleep(duration_in_ms);
+ gpio_direction_output(GPIO_POGO_DATA, 0);
+}
+
+static int pogo_read_id_period(struct tuna_pogo *pogo,
+ unsigned int timeout_in_ms)
+{
+ struct timespec temp;
+ int ret;
+
+ pogo->rise_time.tv_sec = 0;
+ pogo->rise_time.tv_nsec = 0;
+ pogo->fall_time.tv_sec = 0;
+ pogo->fall_time.tv_nsec = 0;
+ pogo->fall_detected = false;
+
+ gpio_direction_input(GPIO_POGO_DATA);
+
+ enable_irq(pogo->data_irq);
+
+ ret = wait_for_completion_timeout(&pogo->completion,
+ msecs_to_jiffies(timeout_in_ms));
+ if (ret <= 0) {
+ if (!pogo->fall_detected)
+ pr_debug("No response to wake within timeout\n");
+ else
+ pr_debug("ID period did not conclude within timeout\n");
+ disable_irq(pogo->data_irq);
+ return -1;
+ }
+
+ temp = timespec_sub(pogo->fall_time, pogo->rise_time);
+ return temp.tv_nsec / NSEC_PER_MSEC;
+}
+
+static irqreturn_t pogo_det_irq_thread(int irq, void *data)
+{
+ struct tuna_pogo *pogo = data;
+ int id_period;
+ unsigned int retry = 0;
+
+ if (gpio_get_value(GPIO_POGO_DET)) {
+ wake_lock(&pogo->wake_lock);
+
+ while (gpio_get_value(GPIO_POGO_DET) &&
+ retry++ <= POGO_DOCK_ID_MAX_RETRY) {
+
+ /* Start the detection process by sending a wake pulse
+ * to the dock.
+ */
+ pogo_send_pulse(POGO_WAKE_PERIOD);
+
+ id_period = pogo_read_id_period(pogo,
+ POGO_ID_PERIOD_TIMEOUT);
+ if (id_period == -1)
+ continue;
+
+ /* The length of the ID period will indicate the type of
+ * dock that is attached.
+ */
+ if (abs(id_period - POGO_ID_CARDOCK) <=
+ POGO_ID_PERIOD_TOLERANCE) {
+ pr_info("POGO Car Dock Detected, ID period"
+ " %dms\n",
+ id_period);
+ pogo->dock_type = POGO_DOCK_CAR;
+ switch_set_state(&pogo->dock_switch,
+ POGO_DOCK_CAR);
+ switch_set_state(&pogo->audio_switch,
+ POGO_AUDIO_CONNECTED);
+ break;
+ } else if (abs(id_period - POGO_ID_DESKDOCK) <=
+ POGO_ID_PERIOD_TOLERANCE) {
+ pr_info("POGO Desk Dock Detected, ID period"
+ " %dms\n",
+ id_period);
+ pogo->dock_type = POGO_DOCK_DESK;
+ switch_set_state(&pogo->dock_switch,
+ POGO_DOCK_DESK);
+ switch_set_state(&pogo->audio_switch,
+ POGO_AUDIO_CONNECTED);
+ break;
+ } else {
+ pr_err("Unknown POGO dock detected, ID period"
+ " %ums\n",
+ id_period);
+ }
+ }
+
+ if (pogo->dock_type == POGO_DOCK_UNDOCKED) {
+ wake_unlock(&pogo->wake_lock);
+ pr_err("Unable to identify pogo dock, giving up\n");
+ return IRQ_HANDLED;
+ }
+
+ /* Instruct the dock to enter SPDIF mode */
+ pogo_send_pulse(POGO_ENTER_SPDIF_PERIOD);
+
+ msleep(POGO_ENTER_SPDIF_WAIT_PERIOD);
+
+ omap_mux_set_gpio(OMAP_MUX_MODE2 | OMAP_PIN_OUTPUT,
+ GPIO_POGO_DATA);
+
+ wake_unlock(&pogo->wake_lock);
+ } else {
+ if (pogo->dock_type != POGO_DOCK_UNDOCKED) {
+ pogo->dock_type = POGO_DOCK_UNDOCKED;
+ pr_info("POGO Dock Detached\n");
+ switch_set_state(&pogo->dock_switch,
+ POGO_DOCK_UNDOCKED);
+ switch_set_state(&pogo->audio_switch,
+ POGO_AUDIO_DISCONNECTED);
+ }
+
+ omap_mux_set_gpio(OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN,
+ GPIO_POGO_DATA);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pogo_data_irq(int irq, void *data)
+{
+ struct tuna_pogo *pogo = data;
+
+ if (gpio_get_value(GPIO_POGO_DATA)) {
+ ktime_get_ts(&pogo->rise_time);
+ irq_set_irq_type(pogo->data_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT);
+ } else {
+ ktime_get_ts(&pogo->fall_time);
+ pogo->fall_detected = true;
+ irq_set_irq_type(pogo->data_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT);
+ complete(&pogo->completion);
+ disable_irq_nosync(pogo->data_irq);
+ }
+ return IRQ_HANDLED;
+}
+
+void __init omap4_tuna_pogo_init(void)
+{
+ struct tuna_pogo *pogo = &tuna_pogo;
+ unsigned int r;
+ int ret;
+
+ omap_mux_init_signal("usbb2_hsic_data.gpio_169",
+ OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3);
+
+ /* The pullup/pulldown controls in the mux register are not the controls
+ * that you are looking for. The usbb2_hsic_data signal has a separate
+ * special control in the CONTROL_USBB_HSIC register.
+ */
+ r = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC);
+ r &= ~OMAP4_USBB2_HSIC_DATA_WD_MASK;
+ omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC);
+
+ ret = gpio_request(GPIO_POGO_DET, "pogo_det");
+ if (ret < 0)
+ pr_err("request for pogo_det gpio failed, err %d\n", ret);
+
+ gpio_direction_input(GPIO_POGO_DET);
+
+ omap_mux_init_signal("abe_dmic_din2.gpio_121",
+ OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3);
+ ret = gpio_request(GPIO_POGO_DATA, "pogo_data");
+ if (ret < 0)
+ pr_err("request for pogo_data gpio failed, err %d\n", ret);
+
+ gpio_direction_output(GPIO_POGO_DATA, 0);
+
+ pogo->dock_switch.name = "dock";
+
+ /* The POGO dock does not involve USB but we are reusing the existing
+ * usb audio switch report the availabilty of SPDIF audio through the
+ * POGO dock.
+ */
+ pogo->audio_switch.name = "usb_audio";
+
+ switch_dev_register(&pogo->dock_switch);
+
+ switch_dev_register(&pogo->audio_switch);
+
+ wake_lock_init(&pogo->wake_lock, WAKE_LOCK_IDLE, "pogo");
+
+ init_completion(&pogo->completion);
+
+ pogo->det_irq = gpio_to_irq(GPIO_POGO_DET);
+
+ ret = request_threaded_irq(pogo->det_irq, NULL,
+ pogo_det_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "pogo_det", pogo);
+ if (ret < 0)
+ pr_err("Unable to register pogo_det interrupt (%d)\n", ret);
+
+ pogo->data_irq = gpio_to_irq(GPIO_POGO_DATA);
+
+ ret = request_irq(pogo->data_irq, pogo_data_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "pogo_data", pogo);
+ if (ret < 0)
+ pr_err("Unable to register pogo_data interrupt (%d)\n", ret);
+
+ disable_irq(pogo->data_irq);
+}
diff --git a/arch/arm/mach-omap2/board-tuna.c b/arch/arm/mach-omap2/board-tuna.c
index a0bbaa7..3e0ff9a 100644
--- a/arch/arm/mach-omap2/board-tuna.c
+++ b/arch/arm/mach-omap2/board-tuna.c
@@ -764,9 +764,6 @@ static void tuna_audio_init(void)
omap_mux_init_signal("gpmc_a24.gpio_48", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3);
omap_mux_init_signal("kpd_col3.gpio_171", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3);
-
- /* McASP for S/PDIF out */
- omap_mux_init_signal("abe_dmic_din2.abe_mcasp_axr", OMAP_PIN_OUTPUT);
}
static struct i2c_board_info __initdata tuna_i2c1_boardinfo[] = {
@@ -1304,6 +1301,7 @@ static void __init tuna_init(void)
omap4_tuna_jack_init();
omap4_tuna_sensors_init();
omap4_tuna_led_init();
+ omap4_tuna_pogo_init();
omap4_tuna_connector_init();
#ifdef CONFIG_OMAP_HSI_DEVICE
if (TUNA_TYPE_MAGURO == omap4_tuna_get_type())
diff --git a/arch/arm/mach-omap2/board-tuna.h b/arch/arm/mach-omap2/board-tuna.h
index 1326810..5f2ec1c 100644
--- a/arch/arm/mach-omap2/board-tuna.h
+++ b/arch/arm/mach-omap2/board-tuna.h
@@ -33,6 +33,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_pogo_init(void);
int omap4_tuna_connector_init(void);
int tuna_wlan_init(void);
int omap_hsi_init(void);