summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Marc Guiraudet <jguiraudet@nvidia.com>2011-10-03 16:42:08 +0200
committerCheryl Jones <chjones@nvidia.com>2011-10-05 15:31:12 -0700
commitd9d20bbd52ddd4332dc6f80647515e9b37c7ffe1 (patch)
treed4d412b6298deccf3b1d2ad69b561ea269fb401d
parent268f5fa89aac00155847c1400f5b2cbea8e19fe2 (diff)
ARM: tegra: Merge icera i450 modem support on whistler
--------------- ARM: tegra: whistler: BB pwr ctrl handover to user space The Icera baseband on Enterprise is powered ON by FIL from user space. There is no need to power ON the baseband from kernel space and it is preferable to not do it as FIL initiates a power cycle of the baseband anyway. Bug 874633 Bug 875299 --------------- ARM: tegra: whistler: change personality condition for ph450 modem Personality check change from 0x3 to 0x2 Bug 877489 Bug 860984 --------------- arm: tegra: ph450 power control via gpio Bug 874633 --------------- arm: tegra: baseband: add callback functions for null phy power off Adding pre_phy_off and post_phy_off callback functions in null_phy_power_off function. So that the modem handshaking GPIO is set to reflect the real phy status. Bug 860984 --------------- ARM: tegra: usb: ulpi support for modem flash-less boot - Update the null phy init sequence: 1) remove ULPI CLK tristate 2) do pinmux bypass immediately after phy reset. 3) check preinit and postinit return value 4) enable disconnect detect - enable utmip phy - Added pre_phy_off and post_phy_off callback functions in null_phy_power_off function. So that the modem handshaking GPIO is set to reflect the real phy status. Bug 860984 --------------- ARM: tegra: whistler: conditionally enable icera PH450 (i450) modem - Modem related init is done conditionally depending on the board personality. - Debug UART init is also done conditionally since UARTA shares pins with ULPI. Bug: 877489 860984 --------------- arm: tegra: Handler for kernel command option personality Adding the handler to parse the kernel command option "personality". --------------- kernel: tegra: whistler-baseband: Change ph450 handshaking protocol Note that you need e450phv2 board Bug 874633 --------------- ARM: tegra: whistler: UARTA pinmux change - UARTA pinx come out of SDIO pingroup.. this is for Icera flashless boot. Change-Id: I4fba6efd455a33d1828cf48f7e0d7dc049df9d14 Reviewed-on: http://git-master/r/55755 Tested-by: Martin Chabot <mchabot@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.c264
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.h31
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h4
-rw-r--r--arch/arm/mach-tegra/usb_phy.c31
4 files changed, 292 insertions, 38 deletions
diff --git a/arch/arm/mach-tegra/board-whistler-baseband.c b/arch/arm/mach-tegra/board-whistler-baseband.c
index bb974d99cedf..456c454c8911 100644
--- a/arch/arm/mach-tegra/board-whistler-baseband.c
+++ b/arch/arm/mach-tegra/board-whistler-baseband.c
@@ -187,8 +187,12 @@ int whistler_baseband_init(void)
static int rainbow_570_reset(void);
static int rainbow_570_handshake(void);
-static int ph450_reset(void);
+static int ph450_power_off(void);
static int ph450_handshake(void);
+static int ph450_phy_on(void);
+static int ph450_phy_off(void);
+
+static struct ph450_priv ph450_priv;
static __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = {
{TEGRA_PINGROUP_UAA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
@@ -199,6 +203,8 @@ static __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = {
TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_UAC, TEGRA_MUX_RSVD4, TEGRA_PUPD_NORMAL,
TEGRA_TRI_NORMAL},
+ {TEGRA_PINGROUP_SDIO1, TEGRA_MUX_UARTA, TEGRA_PUPD_PULL_UP,
+ TEGRA_TRI_NORMAL},
};
static struct tegra_ulpi_trimmer e951_trimmer = { 10, 1, 1, 1 };
@@ -206,8 +212,10 @@ static struct tegra_ulpi_trimmer e951_trimmer = { 10, 1, 1, 1 };
static struct tegra_ulpi_config ehci2_null_ulpi_phy_config = {
.inf_type = TEGRA_USB_NULL_ULPI,
.trimmer = &e951_trimmer,
- .preinit = ph450_reset,
+ .preinit = NULL,
.postinit = ph450_handshake,
+ .post_phy_on = ph450_phy_on,
+ .pre_phy_off = ph450_phy_off,
};
static struct tegra_ehci_platform_data ehci2_null_ulpi_platform_data = {
@@ -216,6 +224,22 @@ static struct tegra_ehci_platform_data ehci2_null_ulpi_platform_data = {
.phy_config = &ehci2_null_ulpi_phy_config,
};
+static int ph450_phy_on(void)
+{
+ /* set AP2MDM_ACK2 low */
+ gpio_set_value(AP2MDM_ACK2, 0);
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static int ph450_phy_off(void)
+{
+ /* set AP2MDM_ACK2 high */
+ gpio_set_value(AP2MDM_ACK2, 1);
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
static int __init tegra_null_ulpi_init(void)
{
tegra_ehci2_device.dev.platform_data = &ehci2_null_ulpi_platform_data;
@@ -291,8 +315,155 @@ static int rainbow_570_handshake(void)
return 0;
}
+static void device_add_handler(struct usb_device *udev)
+{
+ const struct usb_device_descriptor *desc = &udev->descriptor;
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+
+ if (usb_match_id(intf, modem_list)) {
+ pr_info("Add device %d <%s %s>\n", udev->devnum,
+ udev->manufacturer, udev->product);
+
+ mutex_lock(&ph450_priv.lock);
+ ph450_priv.udev = udev;
+ ph450_priv.intf = intf;
+ ph450_priv.vid = desc->idVendor;
+ ph450_priv.pid = desc->idProduct;
+ ph450_priv.wake_cnt = 0;
+ mutex_unlock(&ph450_priv.lock);
+
+ pr_info("persist_enabled: %u\n", udev->persist_enabled);
+
+#if ENABLE_AUTO_SUSPEND
+ usb_enable_autosuspend(udev);
+ pr_info("enable autosuspend for %s %s\n", udev->manufacturer,
+ udev->product);
+#endif
+ }
+}
+
+static void device_remove_handler(struct usb_device *udev)
+{
+ const struct usb_device_descriptor *desc = &udev->descriptor;
+
+ if (desc->idVendor == ph450_priv.vid
+ && desc->idProduct == ph450_priv.pid) {
+ pr_info("Remove device %d <%s %s>\n", udev->devnum,
+ udev->manufacturer, udev->product);
+
+ mutex_lock(&ph450_priv.lock);
+ ph450_priv.udev = NULL;
+ ph450_priv.intf = NULL;
+ ph450_priv.vid = 0;
+ mutex_unlock(&ph450_priv.lock);
+
+#if ENABLE_HOST_RECOVERY
+ queue_delayed_work(ph450_priv.wq, &ph450_priv.power_cycle_work,
+ HZ * 10);
+#endif
+ }
+}
+
+static int usb_notify(struct notifier_block *self, unsigned long action,
+ void *blob)
+{
+ switch (action) {
+ case USB_DEVICE_ADD:
+ device_add_handler(blob);
+ break;
+ case USB_DEVICE_REMOVE:
+ device_remove_handler(blob);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block usb_nb = {
+ .notifier_call = usb_notify,
+};
+
+static irqreturn_t mdm_start_thread(int irq, void *data)
+{
+ struct ph450_priv *priv = (struct ph450_priv *)data;
+
+ if (gpio_get_value(priv->restart_gpio)) {
+ pr_info("BB_RST_OUT high\n");
+ gpio_set_value(AP2MDM_ACK2, 1);
+ /* hold wait lock to complete the enumeration */
+ wake_lock_timeout(&priv->wake_lock, HZ * 2);
+ } else {
+ pr_info("BB_RST_OUT low\n");
+ gpio_set_value(AP2MDM_ACK2, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mdm_wake_thread(int irq, void *data)
+{
+ struct ph450_priv *priv = (struct ph450_priv *)data;
+
+ if (gpio_get_value(priv->wake_gpio) == 0) {
+ pr_info("MDM2AP_ACK2 low\n");
+
+ wake_lock_timeout(&priv->wake_lock, HZ);
+ mutex_lock(&priv->lock);
+ if (priv->udev) {
+ usb_lock_device(priv->udev);
+ pr_info("mdm wake (%u)\n", ++(priv->wake_cnt));
+ if (usb_autopm_get_interface(priv->intf) == 0)
+ usb_autopm_put_interface_async(priv->intf);
+ usb_unlock_device(priv->udev);
+ }
+ mutex_unlock(&priv->lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ph450_power_off(void)
+{
+ pr_info("modem power off\n");
+
+ gpio_set_value(AP2MDM_ACK2, 1);
+ gpio_set_value(MODEM_PWR_ON, 0);
+ return 0;
+}
+
+static int ph450_power_cycle(void)
+{
+ pr_info("modem power cycle\n");
+ mdelay(200);
+ gpio_set_value(AP2MDM_ACK2, 1);
+ gpio_set_value(MODEM_PWR_ON, 0);
+ msleep(100);
+ gpio_set_value(MODEM_PWR_ON, 1);
+ return 0;
+}
+
+static int ph450_handshake(void)
+{
+ /* set AP2MDM_ACK2 low */
+ gpio_set_value(AP2MDM_ACK2, 0);
+
+ return 0;
+}
+
+static void ph450_power_cycle_work_handler(struct work_struct *ws)
+{
+ struct ph450_priv *priv = container_of(ws, struct ph450_priv,
+ power_cycle_work.work);
+
+ mutex_lock(&priv->lock);
+ if (!priv->udev) /* assume modem crashed */
+ ph450_power_cycle();
+ mutex_unlock(&priv->lock);
+}
+
static int __init ph450_init(void)
{
+ int irq;
int ret;
ret = gpio_request(MODEM_PWR_ON, "mdm_power");
@@ -318,40 +489,85 @@ static int __init ph450_init(void)
return ret;
}
- /* enable pull-up for MDM2AP_ACK2 */
- tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_UAC, TEGRA_PUPD_PULL_UP);
+ ret = gpio_request(BB_RST_OUT, "bb_rst_out");
+ if (ret) {
+ gpio_free(MODEM_PWR_ON);
+ gpio_free(MODEM_RESET);
+ gpio_free(AP2MDM_ACK2);
+ gpio_free(MDM2AP_ACK2);
+ return ret;
+ }
+
+ /* enable pull-up for MDM2AP_ACK2 BB_RST_OUT */
+ tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_UAC,
+ TEGRA_PUPD_PULL_UP);
+ tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_GPU,
+ TEGRA_PUPD_PULL_UP);
- tegra_gpio_enable(MODEM_PWR_ON);
+ tegra_gpio_enable(MODEM_PWR_ON);
+ gpio_export(MODEM_PWR_ON, false);
tegra_gpio_enable(MODEM_RESET);
tegra_gpio_enable(AP2MDM_ACK2);
tegra_gpio_enable(MDM2AP_ACK2);
+ tegra_gpio_enable(BB_RST_OUT);
gpio_direction_output(MODEM_PWR_ON, 0);
- gpio_direction_output(MODEM_RESET, 0);
+ gpio_direction_input(MODEM_RESET);
gpio_direction_output(AP2MDM_ACK2, 1);
gpio_direction_input(MDM2AP_ACK2);
+ gpio_direction_input(BB_RST_OUT);
- return 0;
-}
+ /* power off modem */
+ ph450_power_off();
-static int ph450_reset(void)
-{
- gpio_set_value(AP2MDM_ACK2, 1);
- gpio_set_value(MODEM_PWR_ON, 0);
- gpio_set_value(MODEM_PWR_ON, 1);
- mdelay(30);
- gpio_set_value(MODEM_RESET, 0);
- mdelay(200);
- gpio_set_value(MODEM_RESET, 1);
- mdelay(30);
+ ph450_priv.wake_gpio = AP2MDM_ACK2;
+ ph450_priv.restart_gpio = BB_RST_OUT;
- return 1;
-}
+ mutex_init(&(ph450_priv.lock));
+ wake_lock_init(&(ph450_priv.wake_lock), WAKE_LOCK_SUSPEND,
+ "mdm_wake_lock");
-static int ph450_handshake(void)
-{
- /* set AP2MDM_ACK2 low */
- gpio_set_value(AP2MDM_ACK2, 0);
+ /* create work queue */
+ ph450_priv.wq = create_workqueue("mdm_queue");
+ INIT_DELAYED_WORK(&(ph450_priv.power_cycle_work), ph450_power_cycle_work_handler);
+
+ usb_register_notify(&usb_nb);
+
+ /* enable IRQ for BB_RST_OUT */
+ irq = gpio_to_irq(BB_RST_OUT);
+
+ ret = request_threaded_irq(irq, NULL, mdm_start_thread,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "mdm_start", &ph450_priv);
+ if (ret < 0) {
+ pr_err("%s: request_threaded_irq error\n", __func__);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret) {
+ pr_err("%s: enable_irq_wake error\n", __func__);
+ free_irq(irq, &ph450_priv);
+ return ret;
+ }
+
+ /* enable IRQ for MDM2AP_ACK2 */
+ irq = gpio_to_irq(MDM2AP_ACK2);
+
+ ret = request_threaded_irq(irq, NULL, mdm_wake_thread,
+ IRQF_TRIGGER_FALLING, "mdm_wake",
+ &ph450_priv);
+ if (ret < 0) {
+ pr_err("%s: request_threaded_irq error\n", __func__);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret) {
+ pr_err("%s: enable_irq_wake error\n", __func__);
+ free_irq(irq, &ph450_priv);
+ return ret;
+ }
return 0;
}
diff --git a/arch/arm/mach-tegra/board-whistler-baseband.h b/arch/arm/mach-tegra/board-whistler-baseband.h
index 6336ed77d8d4..a7fc4e788cf5 100644
--- a/arch/arm/mach-tegra/board-whistler-baseband.h
+++ b/arch/arm/mach-tegra/board-whistler-baseband.h
@@ -25,10 +25,14 @@
#include <linux/resource.h>
#include <linux/platform_device.h>
#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/usb.h>
+#include <linux/wakelock.h>
#include <asm/mach-types.h>
#include <mach/pinmux.h>
#include <mach/spi.h>
-
#include "clock.h"
#include "devices.h"
#include "gpio-names.h"
@@ -48,6 +52,9 @@
#define MODEM_PWR_ON TEGRA_GPIO_PV1
#define MODEM_RESET TEGRA_GPIO_PV0
+#define ENABLE_AUTO_SUSPEND 0
+#define ENABLE_HOST_RECOVERY 0 /* flashless only feature */
+
/* Rainbow1 and 570 */
#define AWR TEGRA_GPIO_PZ0
#define CWR TEGRA_GPIO_PY6
@@ -57,7 +64,29 @@
/* PH450 */
#define AP2MDM_ACK2 TEGRA_GPIO_PU2
#define MDM2AP_ACK2 TEGRA_GPIO_PV2
+#define BB_RST_OUT TEGRA_GPIO_PV3
+struct ph450_priv {
+unsigned int wake_gpio;
+ unsigned int wake_cnt;
+ unsigned int restart_gpio;
+ struct mutex lock;
+ struct wake_lock wake_lock;
+ unsigned int vid;
+ unsigned int pid;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct workqueue_struct *wq;
+ struct delayed_work power_cycle_work;
+};
+
+static struct usb_device_id modem_list[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
+ .idVendor = 0x1983,
+ },
+ {},
+};
struct whistler_baseband {
struct tegra_clk_init_table *clk_init;
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index 609134c4ff82..7f127fc70402 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -52,6 +52,10 @@ struct tegra_ulpi_config {
const struct tegra_ulpi_trimmer *trimmer;
int (*preinit)(void);
int (*postinit)(void);
+ int (*pre_phy_on)(void);
+ int (*post_phy_on)(void);
+ int (*pre_phy_off)(void);
+ int (*post_phy_off)(void);
};
struct tegra_uhsic_config {
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 0dc14abc3fa4..081b23073937 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -393,17 +393,14 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy)
void __iomem *base = phy->pad_regs;
clk_enable(phy->pad_clk);
-
spin_lock_irqsave(&utmip_pad_lock, flags);
- if (utmip_pad_count++ == 0) {
- val = readl(base + UTMIP_BIAS_CFG0);
- val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
- writel(val, base + UTMIP_BIAS_CFG0);
- }
+ utmip_pad_count++;
+ val = readl(base + UTMIP_BIAS_CFG0);
+ val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
+ writel(val, base + UTMIP_BIAS_CFG0);
spin_unlock_irqrestore(&utmip_pad_lock, flags);
-
clk_disable(phy->pad_clk);
}
@@ -882,9 +879,6 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
void __iomem *base = phy->regs;
struct tegra_ulpi_config *config = phy->config;
- if (config->preinit)
- config->preinit();
-
if (!config->trimmer)
config->trimmer = &default_trimmer;
@@ -896,6 +890,9 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
writel(val, base + ULPI_TIMING_CTRL_0);
+ if (config->pre_phy_on && config->pre_phy_on())
+ return -EAGAIN;
+
val = readl(base + USB_SUSP_CTRL);
val |= ULPI_PHY_ENABLE;
writel(val, base + USB_SUSP_CTRL);
@@ -919,6 +916,7 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
/* enable null phy mode */
val = ULPIS2S_ENA;
val |= ULPIS2S_PLLU_MASTER_BLASTER60;
+ val |= ULPIS2S_SUPPORT_DISCONNECT;
val |= ULPIS2S_SPARE((phy->mode == TEGRA_USB_PHY_MODE_HOST) ? 3 : 1);
writel(val, base + ULPIS2S_CTRL);
@@ -963,20 +961,27 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
USB_PHY_CLK_VALID))
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
- if (config->postinit)
- config->postinit();
+ if (config->post_phy_on && config->post_phy_on())
+ return -EAGAIN;
return 0;
}
-static void null_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
+static int null_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
{
unsigned long val;
void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = phy->config;
+
+ if (config->pre_phy_off && config->pre_phy_off())
+ return -EAGAIN;
val = readl(base + ULPI_TIMING_CTRL_0);
val &= ~ULPI_CLK_PADOUT_ENA;
writel(val, base + ULPI_TIMING_CTRL_0);
+
+ if (config->post_phy_off && config->post_phy_off())
+ return -EAGAIN;
}
static int uhsic_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)