From 71eb4db060a0b5ab35372886a792f41cce53b866 Mon Sep 17 00:00:00 2001 From: Andreas Blaesius Date: Thu, 5 May 2016 13:50:13 +0200 Subject: ARM: OMAP4: add Espresso support Based on the sources from GT-P3110_JB_Opensource, plus a lot of cleanups and commonisation: * 7376125 sec: remove sec_debug * 5bbd0ed espresso10: remove support for chinese espresso variants * 2268c59 [WIP] Unify espresso/espresso10 * f4fc754 Unify espresso/espresso10: connector * e5b79a5 Unify espresso/espresso10: main board file, board detection functions * 33ebb62 espresso: bluetooth: cleanup * e9d2f3d board-espresso-connector.c: add missing espresso10 check * 294e783 espresso: rename espresso_is_espresso10 to board_is_espresso10 * c10890e board-espresso-connector.c: remove broken mhl gpios part * 696751c Remove unnecessary CONFIG_USB_HOST_NOTIFY * 3531c0b espresso: fix sec_muxtbl_init for p5113 variants * 085506f espresso: connector: remove usage of omap_muxtbl * 1bbf98c Remove SII9234 support * ab7e412 Rename SND_OMAP_SOC_WM8994 to SND_OMAP_SOC_ESPRESSO * c839206 espresso: round to closest valid pixel clocks * a8145af panel-ltn: commonize ltn070nl01 and ltn101al03 * 10b9598 espresso: display: update clock settings on init * acceb03 espresso: display: cleanup * d4baea7 espresso: emif: don't duplicate jedec timings * dcb93bd Unify espresso/espresso10: input * 5d5b22d espresso: input: get rid of omap_muxtbl, minor cleanups * b9b6413 Input and sensors layouts for landscape screen orientation * 6d9d26b espresso: irled: remove usage of omap_muxtbl, minor cleanups * a719b1b sec_jack: remove factory test stuff * 3042e05 espresso: switch to twl6030 gpadc * ce44fbe espresso: jack: remove usage of omap_muxtbl * 1d546e4 drivers: modem_if_v2: cleanup * a741bd5 espresso: modem: get rid of omap_muxtbl * 30f92e1 espresso: clean up PMIC board files * 5e3bc1c Unify espresso/espresso10: pmic * 5d02a5e espresso: pmic: fix annoying warnings * f241f28 espresso: pmic: update configuration * fd09f20 ASoC: wm8994: move SND_USE_SUB_MIC compile-time define to pdata * fc5d5a3 espresso: register fixed voltage regulator for mmc1 * 03b338e espresso: pmic: remove omap_muxtbl, minor cleanups * 63594e6 ASoC: omap_wm8994: get rid of omap_muxtbl, not so minor cleanups * 2709b8d espresso: pmic: fix twl6030_power_init on espresso10 * e1a5679 power: max17042_battery: move current range and sdi compensation to pdata * 41cd8c1 espresso: move bootmode setup to board files * e1b8004 espresso: power: remove usage of omap_muxtbl, cleanup * 0672d8d espresso: sdio: cleanup * b34632e Unify espresso/espresso10: sensors * 43ff628 espresso: set sensor orientations via pdata * 754db03 espresso: Update sensor board * f25245b espresso: sensors: get rid of omap_muxtbl, minor cleanups * 3a4cda1 espresso: serial: don't try to register i2c.8 on non-bby variants * f2eed54 espresso: serial: get rid of omap_muxtbl, minor cleanups * 0878522 espresso: vibrator: get rid of omap_muxtbl * 1a28ee3 espresso: update wifi setup * 4456fba espresso: wifi: get rid of omap_muxtbl --- arch/arm/mach-omap2/Kconfig | 9 + arch/arm/mach-omap2/Makefile | 20 + arch/arm/mach-omap2/board-espresso-bluetooth.c | 332 +++++++ arch/arm/mach-omap2/board-espresso-connector.c | 1156 ++++++++++++++++++++++++ arch/arm/mach-omap2/board-espresso-display.c | 195 ++++ arch/arm/mach-omap2/board-espresso-emif.c | 48 + arch/arm/mach-omap2/board-espresso-input.c | 295 ++++++ arch/arm/mach-omap2/board-espresso-irled.c | 320 +++++++ arch/arm/mach-omap2/board-espresso-jack.c | 178 ++++ arch/arm/mach-omap2/board-espresso-modems.c | 285 ++++++ arch/arm/mach-omap2/board-espresso-pmic.c | 617 +++++++++++++ arch/arm/mach-omap2/board-espresso-power.c | 464 ++++++++++ arch/arm/mach-omap2/board-espresso-sdio.c | 102 +++ arch/arm/mach-omap2/board-espresso-sensors.c | 230 +++++ arch/arm/mach-omap2/board-espresso-serial.c | 292 ++++++ arch/arm/mach-omap2/board-espresso-vibrator.c | 154 ++++ arch/arm/mach-omap2/board-espresso-wifi.c | 387 ++++++++ arch/arm/mach-omap2/board-espresso.c | 482 ++++++++++ arch/arm/mach-omap2/board-espresso.h | 94 ++ 19 files changed, 5660 insertions(+) create mode 100644 arch/arm/mach-omap2/board-espresso-bluetooth.c create mode 100644 arch/arm/mach-omap2/board-espresso-connector.c create mode 100644 arch/arm/mach-omap2/board-espresso-display.c create mode 100644 arch/arm/mach-omap2/board-espresso-emif.c create mode 100644 arch/arm/mach-omap2/board-espresso-input.c create mode 100644 arch/arm/mach-omap2/board-espresso-irled.c create mode 100644 arch/arm/mach-omap2/board-espresso-jack.c create mode 100644 arch/arm/mach-omap2/board-espresso-modems.c create mode 100644 arch/arm/mach-omap2/board-espresso-pmic.c create mode 100644 arch/arm/mach-omap2/board-espresso-power.c create mode 100644 arch/arm/mach-omap2/board-espresso-sdio.c create mode 100644 arch/arm/mach-omap2/board-espresso-sensors.c create mode 100644 arch/arm/mach-omap2/board-espresso-serial.c create mode 100644 arch/arm/mach-omap2/board-espresso-vibrator.c create mode 100644 arch/arm/mach-omap2/board-espresso-wifi.c create mode 100644 arch/arm/mach-omap2/board-espresso.c create mode 100644 arch/arm/mach-omap2/board-espresso.h (limited to 'arch') diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 7bb61c5..f947cd7 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -372,6 +372,15 @@ config MACH_OMAP4_PANDA select OMAP_REBOOT_REASON select OMAP_RAM_CONSOLE +config MACH_OMAP4_ESPRESSO + bool "Samsung Espresso / Espresso10 (Galaxy Tab 2) Board" + depends on ARCH_OMAP4 + default n + select OMAP_PACKAGE_CBS + select OMAP_RAM_CONSOLE + select REGULATOR_FIXED_VOLTAGE + select TWL6030_GPADC + config OMAP3_EMU bool "OMAP3 debugging peripherals" depends on ARCH_OMAP3 diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 3e1b519..ab5a215 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -304,6 +304,26 @@ obj-$(CONFIG_MACH_CRANEBOARD) += board-am3517crane.o obj-$(CONFIG_MACH_SBC3530) += board-omap3stalker.o \ hsmmc.o obj-$(CONFIG_MACH_TI8168EVM) += board-ti8168evm.o + +obj-$(CONFIG_MACH_OMAP4_ESPRESSO) += hsmmc.o \ + omap_phy_internal.o \ + board-espresso.o \ + board-espresso-emif.o \ + board-espresso-serial.o \ + board-espresso-pmic.o \ + board-espresso-sdio.o \ + board-espresso-connector.o \ + board-espresso-display.o \ + board-espresso-input.o \ + board-espresso-wifi.o \ + board-espresso-modems.o \ + board-espresso-sensors.o \ + board-espresso-bluetooth.o \ + board-espresso-jack.o \ + board-espresso-power.o \ + board-espresso-vibrator.o \ + board-espresso-irled.o + # Platform specific device init code usbfs-$(CONFIG_ARCH_OMAP_OTG) := usb-fs.o obj-y += $(usbfs-m) $(usbfs-y) diff --git a/arch/arm/mach-omap2/board-espresso-bluetooth.c b/arch/arm/mach-omap2/board-espresso-bluetooth.c new file mode 100644 index 0000000..9e67c87 --- /dev/null +++ b/arch/arm/mach-omap2/board-espresso-bluetooth.c @@ -0,0 +1,332 @@ +/* + * Bluetooth Broadcomm and low power control via GPIO + * + * Copyright (C) 2011 Samsung, Inc. + * Copyright (C) 2011 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GPIO_BT_EN 103 +#define GPIO_BT_NRST 82 +#define GPIO_BT_WAKE 93 +#define GPIO_BT_HOST_WAKE 83 + +static struct rfkill *bt_rfkill; +static bool host_wake_uart_enabled; +static bool wake_uart_enabled; + +struct bcm_bt_lpm { + int wake; + int host_wake; + + struct hrtimer enter_lpm_timer; + ktime_t enter_lpm_delay; + + struct uart_port *uport; + + struct wake_lock wake_lock; + char wake_lock_name[100]; +} bt_lpm; + +static struct gpio bt_gpios[] = { + { + .flags = GPIOF_OUT_INIT_LOW, + .label = "BT_EN", + .gpio = GPIO_BT_EN, + }, + { + .flags = GPIOF_OUT_INIT_LOW, + .label = "BT_nRST", + .gpio = GPIO_BT_NRST, + }, + { + .flags = GPIOF_OUT_INIT_LOW, + .label = "BT_WAKE", + .gpio = GPIO_BT_WAKE, + }, + { + .flags = GPIOF_IN, + .label = "BT_HOST_WAKE", + .gpio = GPIO_BT_HOST_WAKE, + }, +}; + +static int bcm4330_bt_rfkill_set_power(void *data, bool blocked) +{ + /* rfkill_ops callback. Turn transmitter on when blocked is false */ + if (!blocked) { + pr_info("[BT] Bluetooth Power On.\n"); + gpio_set_value(GPIO_BT_EN, 1); + msleep(100); + gpio_set_value(GPIO_BT_NRST, 1); + msleep(50); + + } else { + pr_info("[BT] Bluetooth Power Off.\n"); + gpio_set_value(GPIO_BT_NRST, 0); + gpio_set_value(GPIO_BT_EN, 0); + } + + return 0; +} + +static const struct rfkill_ops bcm4330_bt_rfkill_ops = { + .set_block = bcm4330_bt_rfkill_set_power, +}; + +static void set_wake_locked(int wake) +{ + bt_lpm.wake = wake; + + if (!wake) + wake_unlock(&bt_lpm.wake_lock); + + if (!wake_uart_enabled && wake) + omap_uart_enable(2); + + gpio_set_value(GPIO_BT_WAKE, wake); + + if (wake_uart_enabled && !wake) + omap_uart_disable(2); + + wake_uart_enabled = wake; +} + +static enum hrtimer_restart enter_lpm(struct hrtimer *timer) +{ + unsigned long flags; + spin_lock_irqsave(&bt_lpm.uport->lock, flags); + set_wake_locked(0); + spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + + return HRTIMER_NORESTART; +} + +void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport) +{ + bt_lpm.uport = uport; + + hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer); + + set_wake_locked(1); + + hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay, + HRTIMER_MODE_REL); +} +EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked); + +static void update_host_wake_locked(int host_wake) +{ + if (host_wake == bt_lpm.host_wake) + return; + + bt_lpm.host_wake = host_wake; + + if (host_wake) { + wake_lock(&bt_lpm.wake_lock); + if (!host_wake_uart_enabled) + omap_uart_enable(2); + } else { + if (host_wake_uart_enabled) + omap_uart_disable(2); + /* Take a timed wakelock, so that upper layers can take it. + * The chipset deasserts the hostwake lock, when there is no + * more data to send. + */ + wake_lock_timeout(&bt_lpm.wake_lock, HZ/2); + } + + host_wake_uart_enabled = host_wake; +} + +static irqreturn_t host_wake_isr(int irq, void *dev) +{ + int host_wake; + unsigned long flags; + + host_wake = gpio_get_value(GPIO_BT_HOST_WAKE); + irq_set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + + if (!bt_lpm.uport) { + bt_lpm.host_wake = host_wake; + return IRQ_HANDLED; + } + + spin_lock_irqsave(&bt_lpm.uport->lock, flags); + update_host_wake_locked(host_wake); + spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + + return IRQ_HANDLED; +} + +static int bcm_bt_lpm_init(struct platform_device *pdev) +{ + int irq; + int ret; + + snprintf(bt_lpm.wake_lock_name, sizeof(bt_lpm.wake_lock_name), + "BTLowPower"); + wake_lock_init(&bt_lpm.wake_lock, WAKE_LOCK_SUSPEND, + bt_lpm.wake_lock_name); + hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + bt_lpm.enter_lpm_delay = ktime_set(1, 0); /* 1 sec */ + bt_lpm.enter_lpm_timer.function = enter_lpm; + + bt_lpm.host_wake = 0; + + irq = gpio_to_irq(GPIO_BT_HOST_WAKE); + ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH, + "bt host_wake", NULL); + if (ret) { + gpio_free(GPIO_BT_WAKE); + gpio_free(GPIO_BT_HOST_WAKE); + return ret; + } + + ret = irq_set_irq_wake(irq, 1); + if (ret) { + gpio_free(GPIO_BT_WAKE); + gpio_free(GPIO_BT_HOST_WAKE); + return ret; + } + + gpio_direction_output(GPIO_BT_WAKE, 0); + gpio_direction_input(GPIO_BT_HOST_WAKE); + + return 0; +} + +static int bcm4330_bluetooth_probe(struct platform_device *pdev) +{ + int rc = 0; + int ret = 0; + + gpio_request_array(bt_gpios, ARRAY_SIZE(bt_gpios)); + + gpio_direction_output(GPIO_BT_EN, 0); + gpio_direction_output(GPIO_BT_NRST, 0); + + bt_rfkill = rfkill_alloc("bcm4330 Bluetooth", &pdev->dev, + RFKILL_TYPE_BLUETOOTH, &bcm4330_bt_rfkill_ops, + NULL); + + if (unlikely(!bt_rfkill)) { + gpio_free(GPIO_BT_NRST); + gpio_free(GPIO_BT_EN); + return -ENOMEM; + } + + rfkill_init_sw_state(bt_rfkill, 0); + + rc = rfkill_register(bt_rfkill); + + if (unlikely(rc)) { + rfkill_destroy(bt_rfkill); + gpio_free(GPIO_BT_NRST); + gpio_free(GPIO_BT_EN); + return -1; + } + + rfkill_set_sw_state(bt_rfkill, true); + + ret = bcm_bt_lpm_init(pdev); + if (ret) { + rfkill_unregister(bt_rfkill); + rfkill_destroy(bt_rfkill); + + gpio_free(GPIO_BT_NRST); + gpio_free(GPIO_BT_EN); + } + + return ret; +} + +static int bcm4330_bluetooth_remove(struct platform_device *pdev) +{ + rfkill_unregister(bt_rfkill); + rfkill_destroy(bt_rfkill); + + gpio_free_array(bt_gpios, ARRAY_SIZE(bt_gpios)); + + wake_lock_destroy(&bt_lpm.wake_lock); + return 0; +} + +int bcm4430_bluetooth_suspend(struct platform_device *pdev, pm_message_t state) +{ + int irq = gpio_to_irq(GPIO_BT_HOST_WAKE); + int host_wake; + + disable_irq(irq); + host_wake = gpio_get_value(GPIO_BT_HOST_WAKE); + + if (host_wake) { + enable_irq(irq); + return -EBUSY; + } + + return 0; +} + +int bcm4430_bluetooth_resume(struct platform_device *pdev) +{ + int irq = gpio_to_irq(GPIO_BT_HOST_WAKE); + enable_irq(irq); + return 0; +} + +static struct platform_driver bcm4330_bluetooth_platform_driver = { + .probe = bcm4330_bluetooth_probe, + .remove = bcm4330_bluetooth_remove, + .driver = { + .name = "bcm4330_bluetooth", + .owner = THIS_MODULE, + }, +}; + +static int __init bcm4330_bluetooth_init(void) +{ + return platform_driver_register(&bcm4330_bluetooth_platform_driver); +} +module_init(bcm4330_bluetooth_init); + +static void __exit bcm4330_bluetooth_exit(void) +{ + platform_driver_unregister(&bcm4330_bluetooth_platform_driver); +} +module_exit(bcm4330_bluetooth_exit); + +MODULE_ALIAS("platform:bcm4330"); +MODULE_DESCRIPTION("bcm4330_bluetooth"); +MODULE_AUTHOR("Jaikumar Ganesh "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-omap2/board-espresso-connector.c b/arch/arm/mach-omap2/board-espresso-connector.c new file mode 100644 index 0000000..16d0b40 --- /dev/null +++ b/arch/arm/mach-omap2/board-espresso-connector.c @@ -0,0 +1,1156 @@ +/* arch/arm/mach-omap2/board-espresso-connector.c + * + * Copyright (C) 2011 Samsung Electronics Co, Ltd. + * + * Based on mach-omap2/board-tuna-connector.c + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "board-espresso.h" +#include "mux.h" + +#define GPIO_TA_NCONNECTED 32 + +#define CHARGERUSB_CTRL1 0x8 +#define CHARGERUSB_CTRL3 0xA +#define CHARGERUSB_CINLIMIT 0xE + +#define ESPRESSO_MANUAL_USB_NONE 0 +#define ESPRESSO_MANUAL_USB_MODEM 1 +#define ESPRESSO_MANUAL_USB_AP 2 + +#define ESPRESSO_MANUAL_UART_NONE 0 +#define ESPRESSO_MANUAL_UART_MODEM 1 +#define ESPRESSO_MANUAL_UART_AP 2 + +#define IF_UART_SEL_CP 0 +#define IF_UART_SEL_AP 1 + +#define ADC_CHANNEL_IN0 4 +#define ADC_CHANNEL_IN1 5 +#define ADC_CHANNEL_IN2 6 +#define ADC_CHANNEL_IN3 7 + +#define MAX_ADC_VAL 4096 +#define MIN_ADC_VAL 0 + +#define MASK_SWITCH_USB_AP 0x01 +#define MASK_SWITCH_UART_AP 0x02 + +static char *device_names[] = { + [P30_OTG] = "otg", + [P30_EARJACK_WITH_DOCK] = "earjack", + [P30_CARDOCK] = "car-dock", + [P30_ANAL_TV_OUT] = "TV-Outline", + [P30_KEYBOARDDOCK] = "keboard-dock", + [P30_DESKDOCK] = "desk-dock", + [P30_JIG] = "jig", + [P30_USB] = "USB", + [P30_TA] = "TA", +}; + +struct omap4_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; + struct switch_dev audio_switch; +}; + +static struct omap4_otg espresso_otg_xceiv; + +static struct device *sec_switch_dev; +static int init_switch_sel; + +enum { + UEVENT_DOCK_NONE = 0, + UEVENT_DOCK_DESK, + UEVENT_DOCK_CAR, + UEVENT_DOCK_KEYBOARD = 9, +}; + +enum { + UEVENT_EARJACK_DETACHED = 0, + UEVENT_EARJACK_ATTACHED, +}; + +enum { + GPIO_ACCESSORY_EN = 0, + GPIO_ACCESSORY_INT, + GPIO_DOCK_INT, + GPIO_JIG_ON, +}; + +static struct gpio connector_gpios[] = { + [GPIO_ACCESSORY_EN] = { + .flags = GPIOF_OUT_INIT_LOW, + .gpio = 172, + .label = "ACCESSORY_EN", + }, + [GPIO_ACCESSORY_INT] = { + .flags = GPIOF_IN, + .gpio = 39, + .label = "ACCESSORY_INT_1.8V", + }, + [GPIO_DOCK_INT] = { + .flags = GPIOF_IN, + .gpio = 31, + .label = "DOCK_INT", + }, + [GPIO_JIG_ON] = { + .flags = GPIOF_IN, + .gpio = 55, + .label = "JIG_ON_18", + }, +}; + +enum { + GPIO_USB_SEL1 = 0, + GPIO_USB_SEL2, + GPIO_UART_SEL +}; + +static struct gpio uart_sw_gpios[] = { + [GPIO_USB_SEL1] = { + .flags = GPIOF_OUT_INIT_HIGH, + .gpio = 154, + .label = "USB_SEL1", + }, + [GPIO_USB_SEL2] = { + .flags = GPIOF_OUT_INIT_HIGH, + .gpio = 60, + .label = "USB_SEL2", + }, + [GPIO_UART_SEL] = { + .flags = GPIOF_OUT_INIT_LOW, + .gpio = 47, + .label = "UART_SEL", + }, +}; + +/* STMPE811 */ +static struct i2c_board_info __initdata espresso_i2c6_boardinfo[] = { + { + I2C_BOARD_INFO("stmpe811", 0x82>>1), + }, +}; + +static ssize_t espresso_usb_sel_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t espresso_usb_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size); + +static ssize_t espresso_uart_sel_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t espresso_uart_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size); + +static ssize_t espresso_usb_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t espresso_jig_on_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t espresso_adc_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(usb_sel, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + espresso_usb_sel_show, espresso_usb_sel_store); +static DEVICE_ATTR(uart_sel, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + espresso_uart_sel_show, espresso_uart_sel_store); +static DEVICE_ATTR(usb_state, S_IRUGO, espresso_usb_state_show, NULL); +static DEVICE_ATTR(jig_on, S_IRUSR | S_IWUSR, espresso_jig_on_show, NULL); +static DEVICE_ATTR(adc, S_IRUSR | S_IRGRP, espresso_adc_show, NULL); + +static struct attribute *manual_switch_sel_attributes[] = { + &dev_attr_usb_sel.attr, + &dev_attr_uart_sel.attr, + &dev_attr_usb_state.attr, + &dev_attr_jig_on.attr, + &dev_attr_adc.attr, + NULL, +}; + +static const struct attribute_group manual_switch_sel_group = { + .attrs = manual_switch_sel_attributes, +}; + +static void espresso_set_dock_switch(int state) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + switch_set_state(&espresso_otg->dock_switch, state); +} + +static void espresso_set_audio_switch(int state) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + switch_set_state(&espresso_otg->audio_switch, state); +} + +static void omap4_vusb_enable(struct omap4_otg *otg, bool enable) +{ + /* delay getting the regulator until later */ + if (IS_ERR_OR_NULL(otg->vusb)) { + otg->vusb = regulator_get(&otg->dev, "vusb"); + if (IS_ERR(otg->vusb)) { + dev_err(&otg->dev, "cannot get vusb regulator\n"); + return; + } + } + + if (enable) { + regulator_enable(otg->vusb); + otg->reg_on = true; + } else if (otg->reg_on) { + regulator_disable(otg->vusb); + otg->reg_on = false; + } +} + +static void espresso_accessory_power(u32 device, bool enable) +{ + int gpio_acc_en = connector_gpios[GPIO_ACCESSORY_EN].gpio; + static u32 acc_device; + + /* + token info + 0 : power off, + 1 : Keyboard dock + 2 : USB + */ + + pr_info("accessory_power: acc_device 0x%x, new %d : %s\n", + acc_device, device, enable ? "ON" : "OFF"); + + if (enable) { + acc_device |= (1 << device); + gpio_set_value(gpio_acc_en, 1); + + } else { + + if (device == 0) { + pr_info("accessory_power: force turn off\n"); + gpio_set_value(gpio_acc_en, 0); + + } else { + acc_device &= ~(1 << device); + if (acc_device == 0) { + pr_info("accessory_power: turn off\n"); + gpio_set_value(gpio_acc_en, 0); + } else + pr_info("accessory_power: skip\n"); + } + } +} + +static void espresso_set_vbus_drive(bool enable) +{ + espresso_accessory_power(2, enable); +} + +static void espresso_ap_usb_attach(struct omap4_otg *otg) +{ + omap4_vusb_enable(otg, true); + + otg->otg.default_a = false; + otg->otg.state = OTG_STATE_B_IDLE; + otg->otg.last_event = USB_EVENT_VBUS_CHARGER; + + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_VBUS_CHARGER, + otg->otg.gadget); +} + +static void espresso_ap_usb_detach(struct omap4_otg *otg) +{ + omap4_vusb_enable(otg, false); + + otg->otg.default_a = false; + otg->otg.state = OTG_STATE_B_IDLE; + + if (otg->otg.last_event != USB_EVENT_VBUS_CHARGER) { + otg->otg.last_event = USB_EVENT_NONE; + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_NONE, + otg->otg.gadget); + } else { + otg->otg.last_event = USB_EVENT_NONE; + pr_info("VBUS OFF before detecting the cable type\n"); + } + + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_CHARGER_NONE, + otg->otg.gadget); +} + +static void espresso_usb_host_attach(struct omap4_otg *otg) +{ + pr_info("[%s]\n", __func__); + /* Accessory power down needed */ + espresso_accessory_power(0, 0); + + omap4_vusb_enable(otg, true); + + otg->otg.state = OTG_STATE_A_IDLE; + otg->otg.default_a = true; + otg->otg.last_event = USB_EVENT_ID; + + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_ID, + otg->otg.gadget); +} + +static void espresso_usb_host_detach(struct omap4_otg *otg) +{ + otg->otg.state = OTG_STATE_B_IDLE; + otg->otg.default_a = false; + otg->otg.last_event = USB_EVENT_NONE; + + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_HOST_NONE, + otg->otg.gadget); + + espresso_set_vbus_drive(false); + omap4_vusb_enable(otg, false); +} + +static void espresso_cp_usb_attach(void) +{ + gpio_set_value(uart_sw_gpios[GPIO_USB_SEL1].gpio, 0); + gpio_set_value(uart_sw_gpios[GPIO_USB_SEL2].gpio, 0); + + sysfs_notify(&sec_switch_dev->kobj, NULL, "usb_sel"); +} + +static void espresso_cp_usb_detach(void) +{ + gpio_set_value(uart_sw_gpios[GPIO_USB_SEL1].gpio, 1); + gpio_set_value(uart_sw_gpios[GPIO_USB_SEL2].gpio, 0); + + sysfs_notify(&sec_switch_dev->kobj, NULL, "usb_sel"); +} + +static void espresso_ap_uart_actions(void) +{ + gpio_set_value(uart_sw_gpios[GPIO_UART_SEL].gpio, IF_UART_SEL_AP); +} + +static void espresso_cp_uart_actions(void) +{ + gpio_set_value(uart_sw_gpios[GPIO_UART_SEL].gpio, IF_UART_SEL_CP); +} + +static void espresso_gpio_set_for_adc_check_1(void) +{ + gpio_set_value(uart_sw_gpios[GPIO_USB_SEL1].gpio, 0); + gpio_set_value(uart_sw_gpios[GPIO_USB_SEL2].gpio, 1); +} + +static void espresso_gpio_rel_for_adc_check_1(void) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + if (espresso_otg->usb_manual_mode == ESPRESSO_MANUAL_USB_MODEM) + espresso_cp_usb_attach(); + else + espresso_cp_usb_detach(); +} + +int omap4_espresso_get_adc(enum espresso_adc_ch ch) +{ + int adc; + int i; + int adc_tmp; + int adc_min = MAX_ADC_VAL; + int adc_max = MIN_ADC_VAL; + int adc_sum = 0; + u8 stmpe811_ch = ADC_CHANNEL_IN2; + + if (ch == REMOTE_SENSE) + stmpe811_ch = ADC_CHANNEL_IN1; + else if (ch == ADC_CHECK_1) + stmpe811_ch = ADC_CHANNEL_IN2; + else if (ch == ACCESSORY_ID) + stmpe811_ch = ADC_CHANNEL_IN3; + else if (ch == EAR_ADC_35) + stmpe811_ch = ADC_CHANNEL_IN0; + + if (ch == ADC_CHECK_1) { + /* HQRL Standard defines that time margin from Vbus5V detection + * to ADC_CHECK_1 voltage up should be more than 400ms. + */ + msleep(400); /* delay for unstable cable connection */ + + espresso_gpio_set_for_adc_check_1(); + + msleep(150); /* delay for slow increase of line voltage */ + + for (i = 0; i < 5; i++) { + usleep_range(5000, 5500); + adc_tmp = stmpe811_adc_get_value(stmpe811_ch); + pr_info("adc_check_1 adc=%d\n", adc_tmp); + adc_sum += adc_tmp; + if (adc_max < adc_tmp) + adc_max = adc_tmp; + + if (adc_min > adc_tmp) + adc_min = adc_tmp; + } + espresso_gpio_rel_for_adc_check_1(); + adc = (adc_sum - adc_max - adc_min) / 3; + } else + adc = stmpe811_adc_get_value(stmpe811_ch); + + return adc; +} + +static void espresso_con_usb_charger_attached(struct omap4_otg *otg) +{ + int val; + + /* USB cable connected */ + pr_info("%s, USB_EVENT_VBUS\n", __func__); + + val = gpio_get_value(GPIO_TA_NCONNECTED); + if (val < 0) { + pr_err("usb ta_nconnected: gpio_get_value error %d\n", val); + return; + } + + if (!val) { /* connected */ + otg->otg.default_a = false; + otg->otg.state = OTG_STATE_B_IDLE; + otg->otg.last_event = USB_EVENT_VBUS; + + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_VBUS, otg->otg.gadget); + } else { /* disconnected */ + pr_info("%s, VBUS OFF : USB_EVENT_VBUS is not sent\n", + __func__); + } +} + +static void espresso_con_ta_charger_attached(struct omap4_otg *otg) +{ + int val; + + /* Change to USB_EVENT_CHARGER for sleep */ + pr_info("%s, USB_EVENT_CHARGER\n", __func__); + + val = gpio_get_value(GPIO_TA_NCONNECTED); + if (val < 0) { + pr_err("usb ta_nconnected: gpio_get_value error %d\n", val); + return; + } + + if (!val) { /* connected */ + otg->otg.default_a = false; + otg->otg.state = OTG_STATE_B_IDLE; + otg->otg.last_event = USB_EVENT_CHARGER; + + atomic_notifier_call_chain(&otg->otg.notifier, + USB_EVENT_CHARGER, + otg->otg.gadget); + } else { /* disconnected */ + pr_info("%s, VBUS OFF : USB_EVENT_CHARGER is not sent\n", + __func__); + } +} + +static void espresso_con_charger_detached(void) +{ +} + +static void espresso_deskdock_attached(void) +{ + espresso_set_dock_switch(UEVENT_DOCK_DESK); +} + +static void espresso_deskdock_detached(void) +{ + espresso_set_dock_switch(UEVENT_DOCK_NONE); +} + +static void espresso_30pin_detected(int device, bool connected) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + if (connected) + espresso_otg->current_device |= BIT(device); + else + espresso_otg->current_device &= ~(BIT(device)); + + pr_info("cable detect:%s %s, current device = 0x%04x\n", + device_names[device], (connected) ? "attach" : "detach", + espresso_otg->current_device); + + switch (device) { + case P30_OTG: + if (connected) + espresso_usb_host_attach(espresso_otg); + else + espresso_usb_host_detach(espresso_otg); + break; + case P30_KEYBOARDDOCK: + if (connected) { + espresso_set_dock_switch(UEVENT_DOCK_KEYBOARD); +#ifdef CONFIG_SND_OMAP_SOC_ESPRESSO + notify_dock_status(1); +#endif + } else { + espresso_set_dock_switch(UEVENT_DOCK_NONE); +#ifdef CONFIG_SND_OMAP_SOC_ESPRESSO + notify_dock_status(0); +#endif + } + break; + case P30_DESKDOCK: + if (connected) { + espresso_deskdock_attached(); +#ifdef CONFIG_SND_OMAP_SOC_ESPRESSO + notify_dock_status(1); +#endif + } else { + espresso_deskdock_detached(); +#ifdef CONFIG_SND_OMAP_SOC_ESPRESSO + notify_dock_status(0); +#endif + } + break; + case P30_CARDOCK: + if (connected) { + espresso_set_dock_switch(UEVENT_DOCK_CAR); +#ifdef CONFIG_SND_OMAP_SOC_ESPRESSO + notify_dock_status(1); +#endif + } else { + espresso_set_dock_switch(UEVENT_DOCK_NONE); +#ifdef CONFIG_SND_OMAP_SOC_ESPRESSO + notify_dock_status(0); +#endif + } + break; + case P30_JIG: + if (connected) { + check_jig_status(1); + if (espresso_otg->uart_manual_mode == + ESPRESSO_MANUAL_UART_MODEM) + espresso_cp_uart_actions(); + else + espresso_ap_uart_actions(); + } else + check_jig_status(0); + break; + case P30_USB: + if (connected) + espresso_con_usb_charger_attached(espresso_otg); + else + espresso_con_charger_detached(); + break; + case P30_TA: + if (connected) + espresso_con_ta_charger_attached(espresso_otg); + else + espresso_con_charger_detached(); + break; + case P30_EARJACK_WITH_DOCK: + if (connected) + espresso_set_audio_switch(UEVENT_EARJACK_ATTACHED); + else + espresso_set_audio_switch(UEVENT_EARJACK_DETACHED); + break; + case P30_ANAL_TV_OUT: + pr_warning("This accessory is not supported.\n"); + break; + default: + pr_warning("wrong cable detection information!(%d)\n", device); + break; + } +} + +void omap4_espresso_usb_detected(int cable_type) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + switch (cable_type) { + case CABLE_TYPE_USB: + espresso_30pin_detected(P30_USB, 1); + break; + case CABLE_TYPE_AC: + espresso_30pin_detected(P30_TA, 1); + break; + case CABLE_TYPE_NONE: + if (espresso_otg->current_device & BIT(P30_USB)) + espresso_30pin_detected(P30_USB, 0); + else + espresso_30pin_detected(P30_TA, 0); + break; + default: + pr_warning("wrong usb detected information!\n"); + break; + + } +} + +static s16 espresso_get_accessory_adc(void) +{ + return omap4_espresso_get_adc(ACCESSORY_ID); +} + +/* 30pin connector */ +struct acc_con_platform_data espresso_con_pdata = { + .detected = espresso_30pin_detected, + .get_accessory_adc = espresso_get_accessory_adc, +}; + +struct platform_device espresso_device_connector = { + .name = "acc_con", + .id = -1, + .dev = { + .platform_data = &espresso_con_pdata, + }, +}; + +/* uart_switch, usb_sel */ +static ssize_t espresso_uart_sel_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + const char *mode; + + switch (espresso_otg->uart_manual_mode) { + case ESPRESSO_MANUAL_UART_AP: + mode = "AP"; + break; + case ESPRESSO_MANUAL_UART_MODEM: + mode = "CP"; + break; + default: + mode = "NONE"; + }; + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t espresso_uart_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + mutex_lock(&espresso_otg->lock); + + if (!strncasecmp(buf, "AP", 2)) { + espresso_otg->uart_manual_mode = ESPRESSO_MANUAL_UART_AP; + + if (espresso_otg->current_device & BIT(P30_JIG)) + espresso_ap_uart_actions(); + } else if (!strncasecmp(buf, "CP", 2)) { + espresso_otg->uart_manual_mode = ESPRESSO_MANUAL_UART_MODEM; + + if (espresso_otg->current_device & BIT(P30_JIG)) + espresso_cp_uart_actions(); + } else if (!strncasecmp(buf, "NONE", 4)) { + espresso_otg->uart_manual_mode = ESPRESSO_MANUAL_UART_NONE; + + if (espresso_otg->current_device & BIT(P30_JIG)) + espresso_ap_uart_actions(); + } + + mutex_unlock(&espresso_otg->lock); + + return size; +} + +static ssize_t espresso_usb_sel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + const char *mode; + + switch (espresso_otg->usb_manual_mode) { + case ESPRESSO_MANUAL_USB_AP: + mode = "PDA"; + break; + case ESPRESSO_MANUAL_USB_MODEM: + mode = "MODEM"; + break; + default: + mode = "NONE"; + }; + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t espresso_usb_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + int old_mode; + + mutex_lock(&espresso_otg->lock); + + old_mode = espresso_otg->usb_manual_mode; + + if (!strncasecmp(buf, "PDA", 3)) { + espresso_otg->usb_manual_mode = ESPRESSO_MANUAL_USB_AP; + + /* If we are transitioning from CP USB to AP USB then notify the + * USB stack that is now attached. + */ + if ((espresso_otg->current_device & BIT(P30_USB)) && + (old_mode != ESPRESSO_MANUAL_USB_AP)) { + espresso_cp_usb_detach(); + espresso_ap_usb_attach(espresso_otg); + } + } else if (!strncasecmp(buf, "MODEM", 5)) { + espresso_otg->usb_manual_mode = ESPRESSO_MANUAL_USB_MODEM; + + /* If we are transitioning from AP USB to CP USB then notify the + * USB stack that is has been detached. + */ + if ((espresso_otg->current_device & BIT(P30_USB)) && + (old_mode != ESPRESSO_MANUAL_USB_MODEM)) { + espresso_ap_usb_detach(espresso_otg); + espresso_cp_usb_attach(); + } + } else if (!strncasecmp(buf, "NONE", 5)) { + espresso_otg->usb_manual_mode = ESPRESSO_MANUAL_USB_NONE; + + /* If we are transitioning from CP USB to AP USB then notify the + * USB stack that it is now attached. + */ + if ((espresso_otg->current_device & BIT(P30_USB)) && + (old_mode != ESPRESSO_MANUAL_USB_MODEM)) { + espresso_cp_usb_detach(); + espresso_ap_usb_attach(espresso_otg); + } + } + + mutex_unlock(&espresso_otg->lock); + + return size; +} + +static ssize_t espresso_usb_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + const char *mode; + + if (espresso_otg->current_device & BIT(P30_USB)) + mode = "USB_STATE_CONFIGURED"; + else + mode = "USB_STATE_NOT_CONFIGURED"; + + return sprintf(buf, "%s\n", mode); +} + +static ssize_t espresso_jig_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + const char *mode; + + if (espresso_otg->current_device & BIT(P30_JIG)) + mode = "1"; + else + mode = "0"; + + return sprintf(buf, "%s\n", mode); +} + +/* Factory test application reads /sys/class/sec/switch/adc. + If key_string_on value is 0x1C, the application enables key-strings. + If key_string_on value is 0, the application disables key-strings. + This is Samsung standard. + */ +static ssize_t espresso_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s16 adc_val; + u8 key_string_on; + + adc_val = espresso_get_accessory_adc(); + pr_info("accessory_id adc value = %d\n", adc_val); + + if ((3600 < adc_val) && (adc_val < 3800)) + key_string_on = 0x1C; + else + key_string_on = 0; + + return sprintf(buf, "%x\n", key_string_on); +} + +static int espresso_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 espresso_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 espresso_otg_work(struct work_struct *data) +{ + struct omap4_otg *espresso_otg = + container_of(data, struct omap4_otg, set_vbus_work); + + pr_info("otg %s(%d): current device %04x\n", + __func__, __LINE__, + espresso_otg->current_device); + + mutex_lock(&espresso_otg->lock); + + /* Only allow VBUS drive when in host mode. */ + if (!(espresso_otg->current_device & BIT(P30_OTG))) { + pr_info("otg current device is not USB Host.\n"); + mutex_unlock(&espresso_otg->lock); + return; + } + + espresso_set_vbus_drive(espresso_otg->need_vbus_drive); + + mutex_unlock(&espresso_otg->lock); +} + +static int espresso_otg_set_vbus(struct otg_transceiver *otg, bool enabled) +{ + struct omap4_otg *espresso_otg = + container_of(otg, struct omap4_otg, otg); + dev_dbg(otg->dev, "vbus %s\n", enabled ? "on" : "off"); + + espresso_otg->need_vbus_drive = enabled; + schedule_work(&espresso_otg->set_vbus_work); + + return 0; +} + +static int espresso_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 espresso_otg_phy_shutdown(struct otg_transceiver *otg) +{ + omap4430_phy_power(otg->dev, 0, 0); +} + +static int espresso_otg_set_suspend(struct otg_transceiver *otg, int suspend) +{ + return omap4430_phy_suspend(otg->dev, suspend); +} + +static irqreturn_t ta_nconnected_irq(int irq, void *_otg) +{ + struct omap4_otg *otg = _otg; + int val; + + val = gpio_get_value(GPIO_TA_NCONNECTED); + if (val < 0) { + pr_err("usb ta_nconnected: gpio_get_value error %d\n", val); + return IRQ_HANDLED; + } + + pr_info("usb ta_nconnected_irq : VBUS %s\n", val ? "OFF" : "ON"); + + irq_set_irq_type(irq, val ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + + if (!val) /* connected */ + espresso_ap_usb_attach(otg); + else /* disconnected */ + espresso_ap_usb_detach(otg); + + return IRQ_HANDLED; +} + +static int espresso_vbus_detect_init(struct omap4_otg *otg) +{ + int status = 0; + int irq = 0; + int val; + + status = gpio_request(GPIO_TA_NCONNECTED, "TA_nCONNECTED"); + if (status < 0) { + dev_err(&otg->dev, "gpio %d request failed.\n", GPIO_TA_NCONNECTED); + return status; + } + + status = gpio_direction_input(GPIO_TA_NCONNECTED); + if (status < 0) { + dev_err(&otg->dev, "failed to set gpio %d as input\n", + GPIO_TA_NCONNECTED); + return status; + } + + irq = gpio_to_irq(GPIO_TA_NCONNECTED); + val = gpio_get_value(GPIO_TA_NCONNECTED); + + status = request_threaded_irq(irq, NULL, ta_nconnected_irq, + (val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH) | \ + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "TA_nConnected", otg); + if (status < 0) { + dev_err(&otg->dev, "request irq %d failed for gpio %d\n", + irq, GPIO_TA_NCONNECTED); + return status; + } + + return 0; +} + +/* dock keyboard */ +static struct dock_keyboard_callback { + struct input_dev *dev; + int (*cb) (struct input_dev *dev, bool connected); +} espresso_dock_keyboard_cb; + +static int espresso_dock_keyboard_callback(bool connected) +{ + if (espresso_dock_keyboard_cb.dev && espresso_dock_keyboard_cb.cb) + return espresso_dock_keyboard_cb.cb(espresso_dock_keyboard_cb. + dev, connected); + return 0; +} + +static void espresso_dock_keyboard_power(bool on) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + if (on) { + if (espresso_otg->uart_manual_mode == + ESPRESSO_MANUAL_UART_MODEM) { + pr_info("kbd: switch UART IF to AP\n"); + espresso_ap_uart_actions(); + } + espresso_accessory_power(1, 1); + } else { + espresso_accessory_power(1, 0); + if (espresso_otg->uart_manual_mode == + ESPRESSO_MANUAL_UART_MODEM) { + pr_info("kbd: switch UART IF to CP\n"); + espresso_cp_uart_actions(); + } + } +} + +static void espresso_dock_keyboard_register_cb(struct input_dev *dev, void *cb) +{ + espresso_dock_keyboard_cb.dev = dev; + espresso_dock_keyboard_cb.cb = cb; +} + +struct dock_keyboard_platform_data espresso_dock_keyboard_pdata = { + .power = espresso_dock_keyboard_power, + .register_cb = espresso_dock_keyboard_register_cb, +}; + +struct platform_device espresso_device_dock_keyboard = { + .name = KBD_DRV_NAME, + .id = 0, + .dev = { + .platform_data = &espresso_dock_keyboard_pdata, + }, +}; + +static void __init espresso_switch_initial_setup(void) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + if (init_switch_sel & MASK_SWITCH_UART_AP) { + espresso_ap_uart_actions(); + espresso_otg->uart_manual_mode = ESPRESSO_MANUAL_UART_AP; + } else { + espresso_cp_uart_actions(); + espresso_otg->uart_manual_mode = ESPRESSO_MANUAL_UART_MODEM; + } + + if (init_switch_sel & MASK_SWITCH_USB_AP) { + espresso_cp_usb_detach(); + espresso_otg->usb_manual_mode = ESPRESSO_MANUAL_USB_AP; + } else { + espresso_cp_usb_attach(); + espresso_otg->usb_manual_mode = ESPRESSO_MANUAL_USB_MODEM; + } + +} + +static int __init espresso_save_init_switch_param(char *str) +{ + int ret; + + ret = kstrtoint(str, 10, &init_switch_sel); + if (ret < 0) + return ret; + + return 0; +} +__setup("switch_sel=", espresso_save_init_switch_param); + +static void connector_gpio_init(void) +{ + if ((board_is_espresso10() && system_rev >= 8) || + (!board_is_espresso10() && system_rev >= 10)) { + connector_gpios[GPIO_ACCESSORY_INT].gpio = 2; + } + + gpio_request_array(connector_gpios, ARRAY_SIZE(connector_gpios)); + gpio_request_array(uart_sw_gpios, ARRAY_SIZE(uart_sw_gpios)); +} + +static int __init espresso_plugged_usb_cable_init(void) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + + /* USB connected */ + if (gpio_get_value(GPIO_TA_NCONNECTED) == 0) + omap4_vusb_enable(espresso_otg, true); + + return 0; +} +fs_initcall(espresso_plugged_usb_cable_init); + +void __init omap4_espresso_connector_init(void) +{ + struct omap4_otg *espresso_otg = &espresso_otg_xceiv; + int ret; + + connector_gpio_init(); + mutex_init(&espresso_otg->lock); + INIT_WORK(&espresso_otg->set_vbus_work, espresso_otg_work); + device_initialize(&espresso_otg->dev); + dev_set_name(&espresso_otg->dev, "%s", "espresso_otg"); + ret = device_add(&espresso_otg->dev); + if (ret) { + pr_err("%s: cannot reg device '%s' (%d)\n", __func__, + dev_name(&espresso_otg->dev), ret); + return; + } + + dev_set_drvdata(&espresso_otg->dev, espresso_otg); + + espresso_otg->otg.dev = &espresso_otg->dev; + espresso_otg->otg.label = "espresso_otg_xceiv"; + espresso_otg->otg.set_host = espresso_otg_set_host; + espresso_otg->otg.set_peripheral = espresso_otg_set_peripheral; + espresso_otg->otg.set_suspend = espresso_otg_set_suspend; + espresso_otg->otg.set_vbus = espresso_otg_set_vbus; + espresso_otg->otg.init = espresso_otg_phy_init; + espresso_otg->otg.shutdown = espresso_otg_phy_shutdown; + + ATOMIC_INIT_NOTIFIER_HEAD(&espresso_otg->otg.notifier); + + ret = otg_set_transceiver(&espresso_otg->otg); + if (ret) + pr_err("espresso_otg: cannot set transceiver (%d)\n", ret); + + omap4430_phy_init(&espresso_otg->dev); + + espresso_otg_set_suspend(&espresso_otg->otg, 0); + espresso_vbus_detect_init(espresso_otg); + + sec_switch_dev = device_create(sec_class, NULL, 0, NULL, "switch"); + if (IS_ERR(sec_switch_dev)) { + pr_err("(%s): failed to created device (switch_dev)!\n", + __func__); + goto switch_dev_fail; + } + + ret = sysfs_create_group(&sec_switch_dev->kobj, + &manual_switch_sel_group); + if (ret < 0) + pr_err("fail to create manual switch_sel sysfs group (%d)\n", + ret); +switch_dev_fail: + espresso_switch_initial_setup(); + + /* dock keyboard */ + espresso_dock_keyboard_pdata.dock_irq_gpio = + connector_gpios[GPIO_ACCESSORY_INT].gpio; + + platform_device_register(&espresso_device_dock_keyboard); + + /* ADC IC (STMPE811) */ + i2c_register_board_info(6, espresso_i2c6_boardinfo, + ARRAY_SIZE(espresso_i2c6_boardinfo)); + + /* 30pin connector */ + espresso_con_pdata.accessory_irq_gpio = + connector_gpios[GPIO_ACCESSORY_INT].gpio; + espresso_con_pdata.dock_irq_gpio = connector_gpios[GPIO_DOCK_INT].gpio; + espresso_con_pdata.jig_on_gpio = connector_gpios[GPIO_JIG_ON].gpio; + espresso_con_pdata.dock_keyboard_cb = espresso_dock_keyboard_callback; + + platform_device_register(&espresso_device_connector); + + espresso_otg->dock_switch.name = "dock"; + switch_dev_register(&espresso_otg->dock_switch); + + espresso_otg->audio_switch.name = "usb_audio"; + switch_dev_register(&espresso_otg->audio_switch); + + espresso_otg->current_device = 0; +} diff --git a/arch/arm/mach-omap2/board-espresso-display.c b/arch/arm/mach-omap2/board-espresso-display.c new file mode 100644 index 0000000..60aa7a9 --- /dev/null +++ b/arch/arm/mach-omap2/board-espresso-display.c @@ -0,0 +1,195 @@ +/* arch/arm/mach-omap2/board-espresso-display.c + * + * Copyright (C) 2011 Samsung Electronics Co, Ltd. + * + * Based on mach-omap2/board-tuna-display.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include