blob: 143d14a8721dd27cd1d73762bd84b88b38c3037a [file] [log] [blame]
/*
* arch/arm/mach-tegra/board-whistler-baseband.c
*
* Copyright (C) 2011 NVIDIA Corporation
*
* 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 <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/tegra_caif.h>
#include <mach/tegra_usb_modem_power.h>
#include "board.h"
#include "board-whistler-baseband.h"
static int baseband_phy_on(void);
static int baseband_phy_off(void);
static void baseband_phy_restore_start(void);
static void baseband_phy_restore_end(void);
static struct wake_lock mdm_wake_lock;
static struct gpio modem_gpios[] = {
{MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"},
{MODEM_RESET, GPIOF_IN, "MODEM RESET"},
{BB_RST_OUT, GPIOF_IN, "BB RST OUT"},
{MDM2AP_ACK, GPIOF_IN, "MDM2AP_ACK"},
{AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"},
{AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"},
{ULPI_STP, GPIOF_IN, "ULPI_STP"},
{ULPI_DIR, GPIOF_OUT_INIT_LOW, "ULPI_DIR"},
{ULPI_D0, GPIOF_OUT_INIT_LOW, "ULPI_D0"},
{ULPI_D1, GPIOF_OUT_INIT_LOW, "ULPI_D1"},
};
static __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = {
{TEGRA_PINGROUP_UAA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_UAB, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
TEGRA_TRI_NORMAL},
{TEGRA_PINGROUP_UDA, TEGRA_MUX_ULPI, TEGRA_PUPD_NORMAL,
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 e1219_trimmer = { 10, 1, 1, 1 };
static struct tegra_ulpi_config ehci2_null_ulpi_phy_config = {
.trimmer = &e1219_trimmer,
.post_phy_on = baseband_phy_on,
.pre_phy_off = baseband_phy_off,
.phy_restore_start = baseband_phy_restore_start,
.phy_restore_end = baseband_phy_restore_end,
.phy_restore_gpio = MDM2AP_ACK,
.ulpi_dir_gpio = ULPI_DIR,
.ulpi_d0_gpio = ULPI_D0,
.ulpi_d1_gpio = ULPI_D1,
};
static struct tegra_ehci_platform_data ehci2_null_ulpi_platform_data = {
.operating_mode = TEGRA_USB_HOST,
.power_down_on_bus_suspend = 0,
.phy_config = &ehci2_null_ulpi_phy_config,
.phy_type = TEGRA_USB_PHY_TYPE_NULL_ULPI,
};
static int __init tegra_null_ulpi_init(void)
{
tegra_ehci2_device.dev.platform_data = &ehci2_null_ulpi_platform_data;
platform_device_register(&tegra_ehci2_device);
return 0;
}
static irqreturn_t mdm_start_thread(int irq, void *data)
{
if (gpio_get_value(BB_RST_OUT)) {
pr_info("BB_RST_OUT high\n");
} else {
pr_info("BB_RST_OUT low\n");
/* hold wait lock to complete the enumeration */
wake_lock_timeout(&mdm_wake_lock, HZ * 10);
}
return IRQ_HANDLED;
}
static int baseband_phy_on(void)
{
static bool phy_init;
if (!phy_init) {
/* set AP2MDM_ACK2 low */
gpio_set_value(AP2MDM_ACK2, 0);
phy_init = true;
}
pr_info("%s\n", __func__);
return 0;
}
static int baseband_phy_off(void)
{
pr_info("%s\n", __func__);
return 0;
}
static void baseband_phy_restore_start(void)
{
/* set AP2MDM_ACK2 high */
gpio_set_value(AP2MDM_ACK2, 1);
}
static void baseband_phy_restore_end(void)
{
/* set AP2MDM_ACK2 low */
gpio_set_value(AP2MDM_ACK2, 0);
}
static void baseband_start(void)
{
/*
* Leave baseband powered OFF.
* User-space daemons will take care of powering it up.
*/
pr_info("%s\n", __func__);
gpio_set_value(MODEM_PWR_ON, 0);
}
static void baseband_reset(void)
{
/* Initiate power cycle on baseband sub system */
pr_info("%s\n", __func__);
gpio_set_value(MODEM_PWR_ON, 0);
mdelay(200);
gpio_set_value(MODEM_PWR_ON, 1);
}
static int baseband_init(void)
{
int irq;
int ret;
ret = gpio_request_array(modem_gpios, ARRAY_SIZE(modem_gpios));
if (ret)
return ret;
/* enable pull-up for BB_RST_OUT */
tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_UAC,
TEGRA_PUPD_PULL_UP);
tegra_gpio_enable(MODEM_PWR_ON);
tegra_gpio_enable(MODEM_RESET);
tegra_gpio_enable(AP2MDM_ACK2);
tegra_gpio_enable(BB_RST_OUT);
tegra_gpio_enable(AP2MDM_ACK);
tegra_gpio_enable(MDM2AP_ACK);
tegra_gpio_enable(TEGRA_GPIO_PY3);
tegra_gpio_enable(TEGRA_GPIO_PY1);
tegra_gpio_enable(TEGRA_GPIO_PO1);
tegra_gpio_enable(TEGRA_GPIO_PO2);
/* export GPIO for user space access through sysfs */
gpio_export(MODEM_PWR_ON, false);
/* phy init */
tegra_null_ulpi_init();
wake_lock_init(&mdm_wake_lock, WAKE_LOCK_SUSPEND, "mdm_lock");
/* 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", NULL);
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, NULL);
return ret;
}
return 0;
}
static const struct tegra_modem_operations baseband_operations = {
.init = baseband_init,
.start = baseband_start,
.reset = baseband_reset,
};
static struct tegra_usb_modem_power_platform_data baseband_pdata = {
.ops = &baseband_operations,
.wake_gpio = MDM2AP_ACK2,
.flags = IRQF_TRIGGER_FALLING,
};
static struct platform_device icera_baseband_device = {
.name = "tegra_usb_modem_power",
.id = -1,
.dev = {
.platform_data = &baseband_pdata,
},
};
int __init whistler_baseband_init(void)
{
tegra_pinmux_config_table(whistler_null_ulpi_pinmux,
ARRAY_SIZE(whistler_null_ulpi_pinmux));
platform_device_register(&icera_baseband_device);
return 0;
}