/*
* xhci-tegra.c - Nvidia xHCI host controller driver
*
- * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
#include <linux/usb/otg.h>
#include <linux/clk/tegra.h>
#include <linux/tegra-powergate.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/tegra-fuse.h>
+#include <linux/tegra_pm_domains.h>
#include <mach/tegra_usb_pad_ctrl.h>
#include <mach/tegra_usb_pmc.h>
-#include <mach/pm_domains.h>
#include <mach/mc.h>
#include <mach/xusb.h>
-#include "../../../arch/arm/mach-tegra/iomap.h" /* HACK -- remove */
#include "xhci-tegra.h"
#include "xhci.h"
+#include "../../../arch/arm/mach-tegra/iomap.h"
/* macros */
#define FW_IOCTL_LOG_DEQUEUE_LOW (4)
dev_dbg(_dev, "%s: %s @%x = 0x%x\n", __func__, #_reg, \
_reg, readl(_base + _reg))
-/* PMC register definition */
-#define PMC_PORT_UTMIP_P0 0
-#define PMC_PORT_UTMIP_P1 1
-#define PMC_PORT_UTMIP_P2 2
-#define PMC_PORT_UHSIC_P0 3
-#define PMC_PORT_NUM 4
+#define PMC_PORTMAP_MASK(map, pad) (((map) >> 4*(pad)) & 0xF)
+#define GET_SS_PORTMAP(map, p) (((map) >> 4*(p)) & 0xF)
#define PMC_USB_DEBOUNCE_DEL_0 0xec
#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16)
MBOX_CMD_SET_SS_PWR_GATING,
MBOX_CMD_SET_SS_PWR_UNGATING, /* 8 */
MBOX_CMD_SAVE_DFE_CTLE_CTX,
- MBOX_CMD_AIRPLANE_MODE_ENABLED,
- MBOX_CMD_AIRPLANE_MODE_DISABLED, /* 11 */
+ MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */
+ MBOX_CMD_AIRPLANE_MODE_DISABLED, /* 11, unused */
MBOX_CMD_STAR_HSIC_IDLE,
MBOX_CMD_STOP_HSIC_IDLE,
+ MBOX_CMD_DBC_WAKE_STACK, /* unused */
+ MBOX_CMD_HSIC_PRETEND_CONNECT,
/* needs to be the last cmd */
MBOX_CMD_MAX,
u8 magic[8];
u32 SS_low_power_entry_timeout;
u8 num_hsic_port;
- u8 padding[139]; /* padding bytes to makeup 256-bytes cfgtbl */
+ u8 ss_portmap;
+ u8 padding[138]; /* padding bytes to makeup 256-bytes cfgtbl */
};
struct xusb_save_regs {
bool ctle_ctx_saved[XUSB_SS_PORT_COUNT];
unsigned long last_jiffies;
unsigned long host_phy_base;
+ unsigned long host_phy_size;
void __iomem *host_phy_virt_base;
void __iomem *padctl_base;
struct tegra_xusb_platform_data *pdata;
struct tegra_xusb_board_data *bdata;
+ struct tegra_xusb_chip_calib *cdata;
struct tegra_xusb_padctl_regs *padregs;
+ const struct tegra_xusb_soc_config *soc_config;
+ u64 tegra_xusb_dmamask;
/* mailbox variables */
struct mutex mbox_lock;
u32 cmd_type;
u32 cmd_data;
- struct regulator *xusb_s5p0v_reg;
- struct regulator *xusb_s5p0v1_reg;
- struct regulator *xusb_s5p0v2_reg;
+ struct regulator *xusb_utmi_vbus_regs[XUSB_UTMI_COUNT];
+
struct regulator *xusb_s1p05v_reg;
struct regulator *xusb_s3p3v_reg;
struct regulator *xusb_s1p8v_reg;
struct tegra_xhci_firmware firmware;
struct tegra_xhci_firmware_log log;
+ struct device_attribute hsic_power_attr[XUSB_HSIC_COUNT];
+
+ bool init_done;
};
+static int tegra_xhci_probe2(struct tegra_xhci_hcd *tegra);
+static int tegra_xhci_remove(struct platform_device *pdev);
+static void init_filesystem_firmware_done(const struct firmware *fw,
+ void *context);
+
static struct tegra_usb_pmc_data pmc_data[XUSB_UTMI_COUNT];
static struct tegra_usb_pmc_data pmc_hsic_data[XUSB_HSIC_COUNT];
+static void save_ctle_context(struct tegra_xhci_hcd *tegra,
+ u8 port) __attribute__ ((unused));
+
+#define FIRMWARE_FILE "tegra_xusb_firmware"
+static char *firmware_file = FIRMWARE_FILE;
+#define FIRMWARE_FILE_HELP \
+ "used to specify firmware file of Tegra XHCI host controller. "\
+ "Default value is \"" FIRMWARE_FILE "\"."
+
+module_param(firmware_file, charp, S_IRUGO);
+MODULE_PARM_DESC(firmware_file, FIRMWARE_FILE_HELP);
/* functions */
static inline struct tegra_xhci_hcd *hcd_to_tegra_xhci(struct usb_hcd *hcd)
return (struct tegra_xhci_hcd *) dev_get_drvdata(hcd->self.controller);
}
-#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
static inline void must_have_sync_lock(struct tegra_xhci_hcd *tegra)
{
+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
WARN_ON(tegra->sync_lock.owner != current);
-}
-#else
-static inline void must_have_sync_lock(struct tegra_xhci_hcd *tegra)
#endif
+}
+
+#define for_each_enabled_hsic_pad(_pad, _tegra_xhci_hcd) \
+ for (_pad = find_next_enabled_hsic_pad(_tegra_xhci_hcd, 0); \
+ (_pad < XUSB_HSIC_COUNT) && (_pad >= 0); \
+ _pad = find_next_enabled_hsic_pad(_tegra_xhci_hcd, _pad + 1))
+
+static inline int find_next_enabled_pad(struct tegra_xhci_hcd *tegra,
+ int start, int last)
+{
+ unsigned long portmap = tegra->bdata->portmap;
+ return find_next_bit(&portmap, last , start);
+}
+
+static inline int find_next_enabled_hsic_pad(struct tegra_xhci_hcd *tegra,
+ int curr_pad)
+{
+ int start = XUSB_HSIC_INDEX + curr_pad;
+ int last = XUSB_HSIC_INDEX + XUSB_HSIC_COUNT;
+
+ if ((curr_pad < 0) || (curr_pad >= XUSB_HSIC_COUNT))
+ return -1;
+
+ return find_next_enabled_pad(tegra, start, last) - XUSB_HSIC_INDEX;
+}
static void tegra_xhci_setup_gpio_for_ss_lane(struct tegra_xhci_hcd *tegra)
{
if (BIT(XUSB_UTMI_INDEX + pad) & tegra->bdata->portmap) {
dev_dbg(dev, "%s utmi pad %d\n", __func__, pad);
pmc = &pmc_data[pad];
- pmc->instance = pad;
+ if (tegra->soc_config->pmc_portmap)
+ pmc->instance = PMC_PORTMAP_MASK(
+ tegra->soc_config->pmc_portmap,
+ pad);
+ else
+ pmc->instance = pad;
pmc->phy_type = TEGRA_USB_PHY_INTF_UTMI;
- pmc->port_speed = USB_PMC_PORT_SPEED_HIGH;
+ pmc->port_speed = USB_PMC_PORT_SPEED_UNKNOWN;
pmc->controller_type = TEGRA_USB_3_0;
tegra_usb_pmc_init(pmc);
}
}
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap) {
- dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
-
- pmc = &pmc_hsic_data[pad];
- pmc->instance = pad + 1;
- pmc->phy_type = TEGRA_USB_PHY_INTF_HSIC;
- pmc->port_speed = USB_PMC_PORT_SPEED_HIGH;
- pmc->controller_type = TEGRA_USB_3_0;
- tegra_usb_pmc_init(pmc);
- }
+ for_each_enabled_hsic_pad(pad, tegra) {
+ dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
+ pmc = &pmc_hsic_data[pad];
+ pmc->instance = pad + 1;
+ pmc->phy_type = TEGRA_USB_PHY_INTF_HSIC;
+ pmc->port_speed = USB_PMC_PORT_SPEED_HIGH;
+ pmc->controller_type = TEGRA_USB_3_0;
+ tegra_usb_pmc_init(pmc);
}
-
}
static void pmc_setup_wake_detect(struct tegra_xhci_hcd *tegra)
int port;
int pad;
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap) {
- dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
+ for_each_enabled_hsic_pad(pad, tegra) {
+ dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
- pmc = &pmc_hsic_data[pad];
- port = hsic_pad_to_port(pad);
- portsc = xhci_read_portsc(tegra->xhci, port);
- dev_dbg(dev, "%s hsic pad %d portsc 0x%x\n",
- __func__, pad, portsc);
+ pmc = &pmc_hsic_data[pad];
+ port = hsic_pad_to_port(pad);
+ portsc = xhci_read_portsc(tegra->xhci, port);
+ dev_dbg(dev, "%s hsic pad %d portsc 0x%x\n",
+ __func__, pad, portsc);
- if (((int) portsc != -1) && (portsc & PORT_CONNECT))
- pmc->pmc_ops->setup_pmc_wake_detect(pmc);
- }
+ if (((int) portsc != -1) && (portsc & PORT_CONNECT))
+ pmc->pmc_ops->setup_pmc_wake_detect(pmc);
}
for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
struct device *dev = &tegra->pdev->dev;
int pad;
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap) {
- dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
+ for_each_enabled_hsic_pad(pad, tegra) {
+ dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
- pmc = &pmc_hsic_data[pad];
- pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
- }
+ pmc = &pmc_hsic_data[pad];
+ pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
}
for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
input_addr, data);
}
+static int fw_message_send(struct tegra_xhci_hcd *tegra,
+ enum MBOX_CMD_TYPE type, u32 data)
+{
+ struct device *dev = &tegra->pdev->dev;
+ void __iomem *base = tegra->fpci_base;
+ unsigned long target;
+ u32 reg;
+
+ dev_dbg(dev, "%s type %d data 0x%x\n", __func__, type, data);
+
+ mutex_lock(&tegra->mbox_lock);
+
+ target = jiffies + msecs_to_jiffies(20);
+ /* wait mailbox to become idle, timeout in 20ms */
+ while (((reg = readl(base + XUSB_CFG_ARU_MBOX_OWNER)) != 0) &&
+ time_is_after_jiffies(target)) {
+ mutex_unlock(&tegra->mbox_lock);
+ usleep_range(100, 200);
+ mutex_lock(&tegra->mbox_lock);
+ }
+
+ if (reg != 0) {
+ dev_err(dev, "%s mailbox is still busy\n", __func__);
+ goto timeout;
+ }
+
+ target = jiffies + msecs_to_jiffies(10);
+ /* acquire mailbox , timeout in 10ms */
+ writel(MBOX_OWNER_SW, base + XUSB_CFG_ARU_MBOX_OWNER);
+ while (((reg = readl(base + XUSB_CFG_ARU_MBOX_OWNER)) != MBOX_OWNER_SW)
+ && time_is_after_jiffies(target)) {
+ mutex_unlock(&tegra->mbox_lock);
+ usleep_range(100, 200);
+ mutex_lock(&tegra->mbox_lock);
+ writel(MBOX_OWNER_SW, base + XUSB_CFG_ARU_MBOX_OWNER);
+ }
+
+ if (reg != MBOX_OWNER_SW) {
+ dev_err(dev, "%s acquire mailbox timeout\n", __func__);
+ goto timeout;
+ }
+
+ reg = CMD_TYPE(type) | CMD_DATA(data);
+ writel(reg, base + XUSB_CFG_ARU_MBOX_DATA_IN);
+
+ reg = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
+ reg |= MBOX_INT_EN | MBOX_FALC_INT_EN;
+ writel(reg, tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
+
+ mutex_unlock(&tegra->mbox_lock);
+ return 0;
+
+timeout:
+ reg_dump(dev, base, XUSB_CFG_ARU_MBOX_CMD);
+ reg_dump(dev, base, XUSB_CFG_ARU_MBOX_DATA_IN);
+ reg_dump(dev, base, XUSB_CFG_ARU_MBOX_DATA_OUT);
+ reg_dump(dev, base, XUSB_CFG_ARU_MBOX_OWNER);
+ mutex_unlock(&tegra->mbox_lock);
+ return -ETIMEDOUT;
+}
+
/**
* fw_log_next - find next log entry in a tegra_xhci_firmware_log context.
* This function takes care of wrapping. That means when current log entry
reg |= ((physical_addr >> 16) & 0xffff); /* higher 16-bits */
iowrite32(reg, tegra->fpci_base + XUSB_CFG_ARU_FW_SCRATCH);
- dev_dbg(dev, "new 0x%p physical addr 0x%x\n", deq, physical_addr);
+ dev_dbg(dev, "new 0x%p physical addr 0x%x\n", deq, (u32)physical_addr);
}
static inline bool circ_buffer_full(struct circ_buf *circ)
static inline bool fw_log_wait_empty_timeout(struct tegra_xhci_hcd *tegra,
unsigned timeout)
{
- u32 target = jiffies + msecs_to_jiffies(timeout);
+ unsigned long target = jiffies + msecs_to_jiffies(timeout);
bool ret;
mutex_lock(&tegra->log.mutex);
return -ENOMEM;
}
- dev_info(&pdev->dev, "%d bytes log buffer physical 0x%u virtual 0x%p\n",
- FW_LOG_RING_SIZE, tegra->log.phys_addr, tegra->log.virt_addr);
+ dev_info(&pdev->dev,
+ "%d bytes log buffer physical 0x%u virtual 0x%p\n",
+ FW_LOG_RING_SIZE, (u32)tegra->log.phys_addr,
+ tegra->log.virt_addr);
memset(tegra->log.virt_addr, 0, FW_LOG_RING_SIZE);
tegra->log.dequeue = tegra->log.virt_addr;
static int hsic_power_rail_enable(struct tegra_xhci_hcd *tegra)
{
struct device *dev = &tegra->pdev->dev;
- struct tegra_xusb_regulator_name *supply = &tegra->pdata->bdata->supply;
+ const struct tegra_xusb_regulator_name *supply =
+ &tegra->soc_config->supply;
int ret;
if (tegra->vddio_hsic_reg)
{
struct device *dev = &tegra->pdev->dev;
void __iomem *base = tegra->padctl_base;
- struct tegra_xusb_hsic_config *hsic = &tegra->bdata->hsic;
+ struct tegra_xusb_hsic_config *hsic = &tegra->bdata->hsic[pad];
u32 reg;
if (pad >= XUSB_HSIC_COUNT) {
reg &= ~(PD_RX | HSIC_PD_ZI | PD_TRX | PD_TX);
writel(reg, base + HSIC_PAD_CTL_1(pad));
+ /* Wait for 25 us */
+ usleep_range(25, 50);
+
+ /* Power down tracking circuit */
+ reg = readl(base + HSIC_PAD_CTL_1(pad));
+ reg |= PD_TRX;
+ writel(reg, base + HSIC_PAD_CTL_1(pad));
+
reg = readl(base + HSIC_STRB_TRIM_CONTROL);
reg &= ~(STRB_TRIM_VAL(~0));
reg |= STRB_TRIM_VAL(hsic->strb_trim_val);
return 0;
}
+static void hsic_pad_pretend_connect(struct tegra_xhci_hcd *tegra)
+{
+ struct device *dev = &tegra->pdev->dev;
+ struct tegra_xusb_hsic_config *hsic;
+ struct usb_device *hs_root_hub = tegra->xhci->main_hcd->self.root_hub;
+ int pad;
+ u32 portsc;
+ int port;
+ int enabled_pads = 0;
+ unsigned long wait_ports = 0;
+ unsigned long target;
+
+ for_each_enabled_hsic_pad(pad, tegra) {
+ hsic = &tegra->bdata->hsic[pad];
+ if (hsic->pretend_connect)
+ enabled_pads++;
+ }
+
+ if (enabled_pads == 0) {
+ dev_dbg(dev, "%s no hsic pretend_connect enabled\n", __func__);
+ return;
+ }
+
+ usb_disable_autosuspend(hs_root_hub);
+
+ for_each_enabled_hsic_pad(pad, tegra) {
+ hsic = &tegra->bdata->hsic[pad];
+ if (!hsic->pretend_connect)
+ continue;
+
+ port = hsic_pad_to_port(pad);
+ portsc = xhci_read_portsc(tegra->xhci, port);
+ dev_dbg(dev, "%s pad %u portsc 0x%x\n", __func__, pad, portsc);
+
+ if (!(portsc & PORT_CONNECT)) {
+ /* firmware wants 1-based port index */
+ fw_message_send(tegra,
+ MBOX_CMD_HSIC_PRETEND_CONNECT, BIT(port + 1));
+ }
+
+ set_bit(port, &wait_ports);
+ }
+
+ /* wait till port reaches U0 */
+ target = jiffies + msecs_to_jiffies(500);
+ do {
+ for_each_set_bit(port, &wait_ports, BITS_PER_LONG) {
+ portsc = xhci_read_portsc(tegra->xhci, port);
+ pad = port_to_hsic_pad(port);
+ dev_dbg(dev, "%s pad %u portsc 0x%x\n", __func__,
+ pad, portsc);
+ if ((PORT_PLS_MASK & portsc) == XDEV_U0)
+ clear_bit(port, &wait_ports);
+ }
+
+ if (wait_ports)
+ usleep_range(1000, 5000);
+ } while (wait_ports && time_is_after_jiffies(target));
+
+ if (wait_ports)
+ dev_warn(dev, "%s HSIC pad(s) didn't reach U0.\n", __func__);
+
+ usb_enable_autosuspend(hs_root_hub);
+
+ return;
+}
+
static int hsic_pad_disable(struct tegra_xhci_hcd *tegra, unsigned pad)
{
struct device *dev = &tegra->pdev->dev;
static int tegra_xusb_regulator_init(struct tegra_xhci_hcd *tegra,
struct platform_device *pdev)
{
- struct tegra_xusb_regulator_name *supply = &tegra->bdata->supply;
+ const struct tegra_xusb_regulator_name *supply =
+ &tegra->soc_config->supply;
+ int i;
int err = 0;
tegra->xusb_s3p3v_reg =
}
}
- if ((tegra->bdata->portmap & TEGRA_XUSB_USB2_P0) &&
- !tegra->transceiver) {
- tegra->xusb_s5p0v_reg = devm_regulator_get(&pdev->dev,
- supply->s5p0v);
- if (IS_ERR(tegra->xusb_s5p0v_reg)) {
- dev_err(&pdev->dev, "5p0v regulator not found: %ld."
- , PTR_ERR(tegra->xusb_s5p0v_reg));
- err = PTR_ERR(tegra->xusb_s5p0v_reg);
- goto err_put_s3p3v_reg;
- } else {
- err = regulator_enable(tegra->xusb_s5p0v_reg);
- if (err < 0) {
+ /* enable utmi vbuses */
+ memset(tegra->xusb_utmi_vbus_regs, 0,
+ sizeof(tegra->xusb_utmi_vbus_regs));
+ for (i = 0; i < XUSB_UTMI_COUNT; i++) {
+ struct regulator *reg = NULL;
+ const char *reg_name = supply->utmi_vbuses[i];
+ if (BIT(XUSB_UTMI_INDEX + i) & tegra->bdata->portmap) {
+ if (i == 0 && tegra->transceiver)
+ continue;
+ reg = devm_regulator_get(&pdev->dev, reg_name);
+ if (IS_ERR(reg)) {
dev_err(&pdev->dev,
- "5p0v: regulator enable failed:%d\n",
- err);
- goto err_put_s3p3v_reg;
+ "%s regulator not found: %ld.",
+ reg_name, PTR_ERR(reg));
+ err = PTR_ERR(reg);
+ } else {
+ err = regulator_enable(reg);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "%s: regulator enable failed: %d\n",
+ reg_name, err);
+ }
}
+ if (err)
+ goto err_put_utmi_vbus_reg;
}
+ tegra->xusb_utmi_vbus_regs[i] = reg;
}
tegra->xusb_s1p8v_reg =
dev_err(&pdev->dev, "1p8v regulator not found: %ld."
, PTR_ERR(tegra->xusb_s1p8v_reg));
err = PTR_ERR(tegra->xusb_s1p8v_reg);
- goto err_put_s5p0v_reg;
+ goto err_put_utmi_vbus_reg;
} else {
err = regulator_enable(tegra->xusb_s1p8v_reg);
if (err < 0) {
dev_err(&pdev->dev,
"1p8v: regulator enable failed:%d\n", err);
- goto err_put_s5p0v_reg;
+ goto err_put_utmi_vbus_reg;
}
}
}
}
- if (tegra->bdata->uses_different_vbus_per_port) {
- tegra->xusb_s5p0v1_reg = devm_regulator_get(&pdev->dev,
- supply->s5p0v1);
- if (IS_ERR(tegra->xusb_s5p0v1_reg)) {
- dev_err(&pdev->dev, "5p0v1 regulator not found: %ld."
- , PTR_ERR(tegra->xusb_s5p0v1_reg));
- err = PTR_ERR(tegra->xusb_s5p0v1_reg);
- goto err_put_s1p05v_reg;
- } else {
- if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
- err = regulator_enable(tegra->xusb_s5p0v1_reg);
- if (err < 0) {
- dev_err(&pdev->dev,
- "5p0v1: regulator enable failed:%d\n", err);
- goto err_put_s1p05v_reg;
- }
- }
-
- tegra->xusb_s5p0v2_reg = devm_regulator_get(&pdev->dev,
- supply->s5p0v2);
- if (IS_ERR(tegra->xusb_s5p0v2_reg)) {
- dev_err(&pdev->dev, "5p0v2 regulator not found: %ld."
- , PTR_ERR(tegra->xusb_s5p0v2_reg));
- err = PTR_ERR(tegra->xusb_s5p0v2_reg);
- goto err_put_s1p5v1_reg;
- } else {
- if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
- err = regulator_enable(tegra->xusb_s5p0v2_reg);
- if (err < 0) {
- dev_err(&pdev->dev,
- "5p0v2: regulator enable failed:%d\n", err);
- goto err_put_s1p5v1_reg;
- }
- }
- }
return err;
-err_put_s1p5v1_reg:
- if (tegra->bdata->uses_different_vbus_per_port &&
- tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
- regulator_disable(tegra->xusb_s5p0v1_reg);
-err_put_s1p05v_reg:
- regulator_disable(tegra->xusb_s1p05v_reg);
err_put_s1p8v_reg:
regulator_disable(tegra->xusb_s1p8v_reg);
-err_put_s5p0v_reg:
- if ((tegra->bdata->portmap & TEGRA_XUSB_USB2_P0) && !tegra->transceiver)
- regulator_disable(tegra->xusb_s5p0v_reg);
-err_put_s3p3v_reg:
+err_put_utmi_vbus_reg:
+ for (i = 0; i < XUSB_UTMI_COUNT; i++) {
+ struct regulator *reg = tegra->xusb_utmi_vbus_regs[i];
+ if (!IS_ERR_OR_NULL(reg))
+ regulator_disable(reg);
+ }
regulator_disable(tegra->xusb_s3p3v_reg);
err_null_regulator:
- tegra->xusb_s5p0v_reg = NULL;
- tegra->xusb_s5p0v1_reg = NULL;
- tegra->xusb_s5p0v2_reg = NULL;
+ for (i = 0; i < XUSB_UTMI_COUNT; i++)
+ tegra->xusb_utmi_vbus_regs[i] = NULL;
tegra->xusb_s1p05v_reg = NULL;
tegra->xusb_s3p3v_reg = NULL;
tegra->xusb_s1p8v_reg = NULL;
static void tegra_xusb_regulator_deinit(struct tegra_xhci_hcd *tegra)
{
+ int i;
+
regulator_disable(tegra->xusb_s1p05v_reg);
regulator_disable(tegra->xusb_s1p8v_reg);
- if ((tegra->bdata->portmap & TEGRA_XUSB_USB2_P0) && !tegra->transceiver)
- regulator_disable(tegra->xusb_s5p0v_reg);
- regulator_disable(tegra->xusb_s3p3v_reg);
- if (tegra->bdata->uses_different_vbus_per_port) {
- if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
- regulator_disable(tegra->xusb_s5p0v1_reg);
- if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
- regulator_disable(tegra->xusb_s5p0v2_reg);
+
+ for (i = 0; i < XUSB_UTMI_COUNT; i++) {
+ if (BIT(XUSB_UTMI_INDEX + i) & tegra->bdata->portmap) {
+ struct regulator *reg = tegra->xusb_utmi_vbus_regs[i];
+ if (!IS_ERR_OR_NULL(reg))
+ regulator_disable(reg);
+ tegra->xusb_utmi_vbus_regs[i] = NULL;
+ }
}
+ regulator_disable(tegra->xusb_s3p3v_reg);
+
tegra->xusb_s1p05v_reg = NULL;
tegra->xusb_s1p8v_reg = NULL;
- tegra->xusb_s5p0v_reg = NULL;
- tegra->xusb_s5p0v1_reg = NULL;
- tegra->xusb_s5p0v2_reg = NULL;
tegra->xusb_s3p3v_reg = NULL;
}
return PTR_ERR(tegra->emc_clk);
}
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2) {
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2) {
tegra->pll_re_vco_clk = devm_clk_get(&pdev->dev, "pll_re_vco");
if (IS_ERR(tegra->pll_re_vco_clk)) {
dev_err(&pdev->dev, "Failed to get refPLLE clock\n");
goto get_ss_clk_failed;
}
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2) {
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2) {
err = clk_enable(tegra->pll_re_vco_clk);
if (err) {
dev_err(&pdev->dev, "Failed to enable refPLLE clk\n");
tegra->pll_u_480M = NULL;
get_pll_u_480M_failed:
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
tegra->pll_re_vco_clk = NULL;
get_pll_re_vco_clk_failed:
{
clk_disable(tegra->ss_clk);
clk_disable(tegra->host_clk);
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_disable(tegra->pll_re_vco_clk);
tegra->ss_clk = NULL;
tegra->host_clk = NULL;
tegra->ss_src_clk = NULL;
tegra->clk_m = NULL;
tegra->pll_u_480M = NULL;
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
tegra->pll_re_vco_clk = NULL;
}
int fw_req_rate = rate, cur_rate;
/* Do not handle clock change as needed for HS disconnect issue */
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2) {
- *sw_resp = fw_req_rate | (MBOX_CMD_ACK << MBOX_CMD_SHIFT);
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2) {
+ *sw_resp = CMD_DATA(fw_req_rate) | CMD_TYPE(MBOX_CMD_ACK);
return ret;
}
if (fw_req_rate == cur_rate) {
cmd_ack = MBOX_CMD_ACK;
- *sw_resp = fw_req_rate;
+
} else {
if (clk_handle == tegra->ss_src_clk && fw_req_rate == 12000) {
tegra_xhci_rx_idle_mode_override(tegra, false);
}
- *sw_resp = clk_get_rate(clk_handle);
- *sw_resp /= 1000;
+ cur_rate = (clk_get_rate(clk_handle) / 1000);
- if (*sw_resp != fw_req_rate) {
+ if (cur_rate != fw_req_rate) {
xhci_err(tegra->xhci, "cur_rate=%d, fw_req_rate=%d\n",
cur_rate, fw_req_rate);
cmd_ack = MBOX_CMD_NACK;
}
}
- *sw_resp |= (cmd_ack << MBOX_CMD_SHIFT);
+ *sw_resp = CMD_DATA(cur_rate) | CMD_TYPE(cmd_ack);
return ret;
}
+static void tegra_xusb_set_bw(struct tegra_xhci_hcd *tegra, unsigned int bw)
+{
+ unsigned int freq_khz;
+
+ freq_khz = tegra_emc_bw_to_freq_req(bw);
+ clk_set_rate(tegra->emc_clk, freq_khz * 1000);
+}
+
static void tegra_xhci_save_dfe_context(struct tegra_xhci_hcd *tegra,
u8 port)
{
USB2_OTG_FS_SLEW | USB2_OTG_LS_RSLEW |
USB2_OTG_PD | USB2_OTG_PD2 | USB2_OTG_PD_ZI);
- reg |= tegra->pdata->hs_slew;
- reg |= (port == 2) ? tegra->pdata->ls_rslew_pad2 :
- port ? tegra->pdata->ls_rslew_pad1 :
- tegra->pdata->ls_rslew_pad0;
- reg |= (port == 2) ? tegra->pdata->hs_curr_level_pad2 :
- port ? tegra->pdata->hs_curr_level_pad1 :
- tegra->pdata->hs_curr_level_pad0;
+ reg |= tegra->soc_config->hs_slew;
+ reg |= (port == 2) ? tegra->soc_config->ls_rslew_pad2 :
+ port ? tegra->soc_config->ls_rslew_pad1 :
+ tegra->soc_config->ls_rslew_pad0;
+ reg |= (port == 2) ? tegra->cdata->hs_curr_level_pad2 :
+ port ? tegra->cdata->hs_curr_level_pad1 :
+ tegra->cdata->hs_curr_level_pad0;
writel(reg, tegra->padctl_base + ctl0_offset);
reg = readl(tegra->padctl_base + ctl1_offset);
| USB2_OTG_PD_CHRP_FORCE_POWERUP
| USB2_OTG_PD_DISC_FORCE_POWERUP
| USB2_OTG_PD_DR);
- reg |= (tegra->pdata->hs_iref_cap << 9) |
- (tegra->pdata->hs_term_range_adj << 3);
+ reg |= (tegra->cdata->hs_iref_cap << 9) |
+ (tegra->cdata->hs_term_range_adj << 3);
writel(reg, tegra->padctl_base + ctl1_offset);
/*Release OTG port if not in host mode*/
tegra_xhci_release_otg_port(true);
}
+static inline bool xusb_use_sata_lane(struct tegra_xhci_hcd *tegra)
+{
+ return ((XUSB_DEVICE_ID_T114 == tegra->device_id) ? false
+ : ((tegra->bdata->portmap & TEGRA_XUSB_SS_P1)
+ && (tegra->bdata->lane_owner & BIT(0))));
+}
+
static void tegra_xhci_program_ss_pad(struct tegra_xhci_hcd *tegra,
u8 port)
{
reg = readl(tegra->padctl_base + ctl2_offset);
reg &= ~(IOPHY_USB3_RXWANDER | IOPHY_USB3_RXEQ |
IOPHY_USB3_CDRCNTL);
- reg |= tegra->pdata->rx_wander | tegra->pdata->rx_eq |
- tegra->pdata->cdr_cntl;
+ reg |= tegra->soc_config->rx_wander | tegra->soc_config->rx_eq |
+ tegra->soc_config->cdr_cntl;
writel(reg, tegra->padctl_base + ctl2_offset);
reg = readl(tegra->padctl_base + ctl4_offset);
- reg = tegra->pdata->dfe_cntl;
+ reg = tegra->soc_config->dfe_cntl;
writel(reg, tegra->padctl_base + ctl4_offset);
reg = readl(tegra->padctl_base + ctl5_offset);
reg = readl(tegra->padctl_base + MISC_PAD_CTL_2_0(port));
reg &= ~SPARE_IN(~0);
- reg |= SPARE_IN(tegra->pdata->spare_in);
+ reg |= SPARE_IN(tegra->soc_config->spare_in);
writel(reg, tegra->padctl_base + MISC_PAD_CTL_2_0(port));
+ if (xusb_use_sata_lane(tegra)) {
+ reg = readl(tegra->padctl_base + MISC_PAD_S0_CTL_5_0);
+ reg |= RX_QEYE_EN;
+ writel(reg, tegra->padctl_base + MISC_PAD_S0_CTL_5_0);
+
+ reg = readl(tegra->padctl_base + MISC_PAD_S0_CTL_2_0);
+ reg &= ~SPARE_IN(~0);
+ reg |= SPARE_IN(tegra->soc_config->spare_in);
+ writel(reg, tegra->padctl_base + MISC_PAD_S0_CTL_2_0);
+ }
+
reg = readl(tegra->padctl_base + padregs->ss_port_map_0);
reg &= ~(port ? SS_PORT_MAP_P1 : SS_PORT_MAP_P0);
reg |= (tegra->bdata->ss_portmap &
(port ? TEGRA_XUSB_SS1_PORT_MAP : TEGRA_XUSB_SS0_PORT_MAP));
writel(reg, tegra->padctl_base + padregs->ss_port_map_0);
+ /* Make sure the SS port capability set correctly */
+ reg = readl(tegra->padctl_base + padregs->usb2_port_cap_0);
+ reg &= ~USB2_PORT_CAP_MASK(
+ GET_SS_PORTMAP(tegra->bdata->ss_portmap, port));
+ reg |= USB2_PORT_CAP_HOST(
+ GET_SS_PORTMAP(tegra->bdata->ss_portmap, port));
+ writel(reg, tegra->padctl_base + padregs->usb2_port_cap_0);
+
tegra_xhci_restore_dfe_context(tegra, port);
tegra_xhci_restore_ctle_context(tegra, port);
}
reg = readl(tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
reg &= ~(USB2_BIAS_HS_SQUELCH_LEVEL | USB2_BIAS_HS_DISCON_LEVEL);
- reg |= tegra->pdata->hs_squelch_level | tegra->pdata->hs_disc_lvl;
+ reg |= tegra->cdata->hs_squelch_level | tegra->soc_config->hs_disc_lvl;
writel(reg, tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
reg = readl(tegra->padctl_base + padregs->snps_oc_map_0);
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
tegra_xhci_program_utmip_pad(tegra, 2);
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap)
- hsic_pad_enable(tegra, pad);
- }
+ for_each_enabled_hsic_pad(pad, tegra)
+ hsic_pad_enable(tegra, pad);
if (tegra->bdata->portmap & TEGRA_XUSB_ULPI_P0)
tegra_xhci_program_ulpi_pad(tegra, 0);
static void tegra_xhci_enable_fw_message(struct tegra_xhci_hcd *tegra)
{
- struct platform_device *pdev = tegra->pdev;
- u32 reg, timeout = 0xff, cmd;
-
- mutex_lock(&tegra->mbox_lock);
-
- do {
- writel(MBOX_OWNER_SW,
- tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
- reg = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
- usleep_range(10, 20);
- } while (reg != MBOX_OWNER_SW && timeout--);
-
- if ((timeout == 0) && (reg != MBOX_OWNER_SW)) {
- dev_err(&pdev->dev, "Failed to set mbox message owner ID\n");
- mutex_unlock(&tegra->mbox_lock);
- return;
- }
-
- writel((MBOX_CMD_MSG_ENABLED << MBOX_CMD_SHIFT),
- tegra->fpci_base + XUSB_CFG_ARU_MBOX_DATA_IN);
-
- cmd = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
- cmd |= MBOX_INT_EN | MBOX_FALC_INT_EN;
- writel(cmd, tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
-
- mutex_unlock(&tegra->mbox_lock);
+ fw_message_send(tegra, MBOX_CMD_MSG_ENABLED, 0 /* no data needed */);
}
static int load_firmware(struct tegra_xhci_hcd *tegra, bool resetARU)
u32 usbsts, count = 0xff;
struct xhci_cap_regs __iomem *cap_regs;
struct xhci_op_regs __iomem *op_regs;
+ int pad;
- /* enable mbox interrupt */
- writel(readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD) | MBOX_INT_EN,
- tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
+ /* Program SS port map config */
+ cfg_tbl->ss_portmap = 0x0;
+ cfg_tbl->ss_portmap |=
+ (tegra->bdata->portmap & ((1 << XUSB_SS_PORT_COUNT) - 1));
/* First thing, reset the ARU. By the time we get to
* loading boot code below, reset would be complete.
fw_tm.tm_min, fw_tm.tm_sec,
csb_read(tegra, XUSB_FALC_CPUCTL));
+ cfg_tbl->num_hsic_port = 0;
+ for_each_enabled_hsic_pad(pad, tegra)
+ cfg_tbl->num_hsic_port++;
+
dev_dbg(&pdev->dev, "num_hsic_port %d\n", cfg_tbl->num_hsic_port);
/* return fail if firmware status is not good */
dev_err(&pdev->dev, "Controller not ready\n");
return -EFAULT;
}
-
- /* TODO move this into firmware to avoid race */
- writel(0x0, tegra->fpci_base + XUSB_CFG_ARU_C11PAGESEL0);
- writel(0x1000, tegra->fpci_base + XUSB_CFG_ARU_C11PAGESEL1);
- writel(0x10, tegra->fpci_base + XUSB_CFG_HSPX_CORE_HSICWRAP);
- reg_dump(&pdev->dev, tegra->fpci_base, XUSB_CFG_ARU_C11PAGESEL0);
- reg_dump(&pdev->dev, tegra->fpci_base, XUSB_CFG_ARU_C11PAGESEL1);
- reg_dump(&pdev->dev, tegra->fpci_base, XUSB_CFG_HSPX_CORE_HSICWRAP);
+ for_each_enabled_hsic_pad(pad, tegra)
+ hsic_pad_pupd_set(tegra, pad, PUPD_IDLE);
return 0;
}
must_have_sync_lock(tegra);
+ /* update maximum BW requirement to 0 */
+ tegra_xusb_set_bw(tegra, 0);
+
/* This is SS partition ELPG entry
* STEP 0: firmware will set WOC WOD bits in PVTPORTSC2 regs.
*/
xhci_dbg(xhci, "%s: PMC_UTMIP_UHSIC_SLEEP_CFG_0 = %x\n", __func__,
tegra_usb_pmc_reg_read(PMC_UTMIP_UHSIC_SLEEP_CFG_0));
- /* STEP 4: Assert reset to host clk and disable host clk */
- tegra_periph_reset_assert(tegra->host_clk);
-
- clk_disable(tegra->host_clk);
-
- /* wait 150us */
- usleep_range(150, 200);
-
- /* flush MC client of XUSB_HOST */
- tegra_powergate_mc_flush(TEGRA_POWERGATE_XUSBC);
-
- /* STEP 4: Powergate host partition */
/* tegra_powergate_partition also does partition reset assert */
ret = tegra_powergate_partition(TEGRA_POWERGATE_XUSBC);
if (ret) {
return ret;
}
tegra->host_pwr_gated = true;
+ clk_disable(tegra->host_clk);
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_disable(tegra->pll_re_vco_clk);
clk_disable(tegra->emc_clk);
/* set port ownership to SNPS */
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg, utmip_rctrl_val, utmip_tctrl_val, pad_mux, portmux, portowner;
- int port;
portmux = USB2_OTG_PAD_PORT_MASK(0) | USB2_OTG_PAD_PORT_MASK(1);
portowner = USB2_OTG_PAD_PORT_OWNER_XUSB(0) |
* tctrl_val = 0x1f - (16 - ffz(utmip_tctrl_val)
* rctrl_val = 0x1f - (16 - ffz(utmip_rctrl_val)
*/
- for (port = 0; port < XUSB_UTMI_COUNT; port++) {
- pmc_data[port].utmip_rctrl_val =
- 0xf + ffz(utmip_rctrl_val);
- pmc_data[port].utmip_tctrl_val =
- 0xf + ffz(utmip_tctrl_val);
- xhci_dbg(tegra->xhci, "rctrl_val = 0x%x, tctrl_val = 0x%x\n",
- pmc_data[port].utmip_rctrl_val,
- pmc_data[port].utmip_tctrl_val);
- }
+ utmip_rctrl_val = 0xf + ffz(utmip_rctrl_val);
+ utmip_tctrl_val = 0xf + ffz(utmip_tctrl_val);
+ utmi_phy_update_trking_data(utmip_tctrl_val, utmip_rctrl_val);
+ xhci_dbg(tegra->xhci, "rctrl_val = 0x%x, tctrl_val = 0x%x\n",
+ utmip_rctrl_val, utmip_tctrl_val);
/* XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0::PD = 1 and
* XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0::PD_TRK = 1
writel(reg, tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
/* Program these values into PMC regiseter and program the
- * PMC override. This will be done as part of pmc setup
+ * PMC override.
*/
+ reg = PMC_TCTRL_VAL(utmip_tctrl_val) |
+ PMC_RCTRL_VAL(utmip_rctrl_val);
+ tegra_usb_pmc_reg_update(PMC_UTMIP_TERM_PAD_CFG,
+ 0xffffffff, reg);
+ reg = UTMIP_RCTRL_USE_PMC_P2 | UTMIP_TCTRL_USE_PMC_P2;
+ tegra_usb_pmc_reg_update(PMC_SLEEP_CFG, reg, reg);
} else {
- /* TODO use common PMC API to use SNPS register space */
+ /* Use common PMC API to use SNPS register space */
+ utmi_phy_set_snps_trking_data();
}
}
return 0;
clk_enable(tegra->emc_clk);
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_enable(tegra->pll_re_vco_clk);
if (tegra->lp0_exit) {
csb_read(tegra, XUSB_FALC_FS_PVTPORTSC3));
debug_print_portsc(xhci);
+ tegra_xhci_enable_fw_message(tegra);
ret = load_firmware(tegra, false /* EPLG exit, do not reset ARU */);
if (ret < 0) {
xhci_err(xhci, "%s: failed to load firmware %d\n",
struct tegra_xhci_hcd *tegra = container_of(work, struct tegra_xhci_hcd,
mbox_work);
struct xhci_hcd *xhci = tegra->xhci;
- unsigned int freq_khz;
int pad, port;
unsigned long ports;
+ enum MBOX_CMD_TYPE response;
mutex_lock(&tegra->mbox_lock);
- /* get the owner id */
- tegra->mbox_owner = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
- tegra->mbox_owner &= MBOX_OWNER_ID_MASK;
-
/* get the mbox message from firmware */
fw_msg = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_DATA_OUT);
data_in = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_DATA_IN);
if (data_in) {
+ dev_warn(&tegra->pdev->dev, "%s data_in 0x%x\n",
+ __func__, data_in);
mutex_unlock(&tegra->mbox_lock);
return;
}
/* get cmd type and cmd data */
- tegra->cmd_type = (fw_msg & MBOX_CMD_TYPE_MASK) >> MBOX_CMD_SHIFT;
- tegra->cmd_data = (fw_msg & MBOX_CMD_DATA_MASK);
+ tegra->cmd_type = (fw_msg >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
+ tegra->cmd_data = (fw_msg >> CMD_DATA_SHIFT) & CMD_DATA_MASK;
/* decode the message and make appropriate requests to
* clock or powergating module.
xhci_err(xhci, "%s: could not set required ss rate.\n",
__func__);
goto send_sw_response;
+
case MBOX_CMD_SET_BW:
/* fw sends BW request in MByte/sec */
- freq_khz = tegra_emc_bw_to_freq_req(tegra->cmd_data << 10);
- clk_set_rate(tegra->emc_clk, freq_khz * 1000);
-
- /* clear MBOX_SMI_INT_EN bit */
- cmd = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
- cmd &= ~MBOX_SMI_INT_EN;
- writel(cmd, tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
-
- /* clear mbox owner as ACK will not be sent for this request */
- writel(0, tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
+ mutex_lock(&tegra->sync_lock);
+ tegra_xusb_set_bw(tegra, tegra->cmd_data << 10);
+ mutex_unlock(&tegra->sync_lock);
break;
+
case MBOX_CMD_SAVE_DFE_CTLE_CTX:
tegra_xhci_save_dfe_context(tegra, tegra->cmd_data);
tegra_xhci_save_ctle_context(tegra, tegra->cmd_data);
- sw_resp |= tegra->cmd_data | (MBOX_CMD_ACK << MBOX_CMD_SHIFT);
+ sw_resp = CMD_DATA(tegra->cmd_data) | CMD_TYPE(MBOX_CMD_ACK);
goto send_sw_response;
case MBOX_CMD_STAR_HSIC_IDLE:
- ports = sw_resp = tegra->cmd_data;
- for_each_set_bit(port, &ports, MBOX_CMD_SHIFT) {
+ ports = tegra->cmd_data;
+ for_each_set_bit(port, &ports, BITS_PER_LONG) {
pad = port_to_hsic_pad(port - 1);
mutex_lock(&tegra->sync_lock);
ret = hsic_pad_pupd_set(tegra, pad, PUPD_IDLE);
break;
}
+ sw_resp = CMD_DATA(tegra->cmd_data);
if (!ret)
- sw_resp |= (MBOX_CMD_ACK << MBOX_CMD_SHIFT);
+ sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
else
- sw_resp |= (MBOX_CMD_NACK << MBOX_CMD_SHIFT);
+ sw_resp |= CMD_TYPE(MBOX_CMD_NACK);
goto send_sw_response;
case MBOX_CMD_STOP_HSIC_IDLE:
- ports = sw_resp = tegra->cmd_data;
- for_each_set_bit(port, &ports, MBOX_CMD_SHIFT) {
+ ports = tegra->cmd_data;
+ for_each_set_bit(port, &ports, BITS_PER_LONG) {
pad = port_to_hsic_pad(port - 1);
mutex_lock(&tegra->sync_lock);
ret = hsic_pad_pupd_set(tegra, pad, PUPD_DISABLE);
break;
}
+ sw_resp = CMD_DATA(tegra->cmd_data);
if (!ret)
- sw_resp |= (MBOX_CMD_ACK << MBOX_CMD_SHIFT);
+ sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
else
- sw_resp |= (MBOX_CMD_NACK << MBOX_CMD_SHIFT);
-
+ sw_resp |= CMD_TYPE(MBOX_CMD_NACK);
goto send_sw_response;
case MBOX_CMD_ACK:
- writel(0, tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
- writel(0, tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
+ xhci_dbg(xhci, "%s firmware responds with ACK\n", __func__);
break;
case MBOX_CMD_NACK:
- writel(0, tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
- writel(0, tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
+ xhci_warn(xhci, "%s firmware responds with NACK\n", __func__);
break;
default:
xhci_err(xhci, "%s: invalid cmdtype %d\n",
__func__, tegra->cmd_type);
}
+
+ /* clear MBOX_SMI_INT_EN bit */
+ cmd = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
+ cmd &= ~MBOX_SMI_INT_EN;
+ writel(cmd, tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
+
+ /* clear mailbox ownership */
+ writel(0, tegra->fpci_base + XUSB_CFG_ARU_MBOX_OWNER);
+
mutex_unlock(&tegra->mbox_lock);
return;
send_sw_response:
- if (((sw_resp & MBOX_CMD_TYPE_MASK) >> MBOX_CMD_SHIFT) == MBOX_CMD_NACK)
- xhci_err(xhci, "%s respond fw message 0x%x with NAK\n",
- __func__, fw_msg);
+ response = (sw_resp >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
+ if (response == MBOX_CMD_NACK)
+ xhci_warn(xhci, "%s respond fw message 0x%x with NACK\n",
+ __func__, fw_msg);
+ else if (response == MBOX_CMD_ACK)
+ xhci_dbg(xhci, "%s respond fw message 0x%x with ACK\n",
+ __func__, fw_msg);
+ else
+ xhci_err(xhci, "%s respond fw message 0x%x with %d\n",
+ __func__, fw_msg, response);
writel(sw_resp, tegra->fpci_base + XUSB_CFG_ARU_MBOX_DATA_IN);
cmd = readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
*/
temp = readl(tegra->fpci_base + XUSB_CFG_ARU_SMI_INTR);
-
- /* write 1 to clear SMI INTR en bit ( bit 3 ) */
- temp = MBOX_SMI_INTR_EN;
writel(temp, tegra->fpci_base + XUSB_CFG_ARU_SMI_INTR);
- schedule_work(&tegra->mbox_work);
+ xhci_dbg(tegra->xhci, "SMI INTR status 0x%x\n", temp);
+ if (temp & SMI_INTR_STATUS_FW_REINIT)
+ xhci_err(tegra->xhci, "Firmware reinit.\n");
+ if (temp & SMI_INTR_STATUS_MBOX)
+ schedule_work(&tegra->mbox_work);
spin_unlock(&tegra->lock);
return IRQ_HANDLED;
* here that the generic code does not try to make a pci_dev from our
* dev struct in order to setup MSI
*/
- xhci->quirks |= XHCI_BROKEN_MSI;
+ xhci->quirks |= XHCI_PLAT;
xhci->quirks &= ~XHCI_SPURIOUS_REBOOT;
}
}
static int tegra_xhci_request_mem_region(struct platform_device *pdev,
- const char *name, void __iomem **region)
+ int num, void __iomem **region)
{
struct resource *res;
void __iomem *mem;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, num);
if (!res) {
- dev_err(&pdev->dev, "memory resource %s doesn't exist\n", name);
+ dev_err(&pdev->dev, "memory resource %d doesn't exist\n", num);
return -ENODEV;
}
mem = devm_request_and_ioremap(&pdev->dev, res);
if (!mem) {
- dev_err(&pdev->dev, "failed to ioremap for %s\n", name);
+ dev_err(&pdev->dev, "failed to ioremap for %d\n", num);
return -EFAULT;
}
*region = mem;
}
static int tegra_xhci_request_irq(struct platform_device *pdev,
- const char *rscname, irq_handler_t handler, unsigned long irqflags,
+ int num, irq_handler_t handler, unsigned long irqflags,
const char *devname, int *irq_no)
{
int ret;
struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
struct resource *res;
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, rscname);
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, num);
if (!res) {
- dev_err(&pdev->dev, "irq resource %s doesn't exist\n", rscname);
+ dev_err(&pdev->dev, "irq resource %d doesn't exist\n", num);
return -ENODEV;
}
if (ret != 0) {
dev_err(&pdev->dev,
"failed to request_irq for %s (irq %d), error = %d\n",
- devname, res->start, ret);
+ devname, (int)res->start, ret);
return ret;
}
*irq_no = res->start;
int ret = 0;
mutex_lock(&tegra->sync_lock);
+ if (!tegra->init_done) {
+ xhci_warn(xhci, "%s: xhci probe not done\n",
+ __func__);
+ mutex_unlock(&tegra->sync_lock);
+ return -EBUSY;
+ }
if (!tegra->hc_in_elpg) {
xhci_warn(xhci, "%s: lp0 suspend entry while elpg not done\n",
__func__);
}
mutex_unlock(&tegra->sync_lock);
- tegra_xhci_ss_wake_on_interrupts(tegra->bdata->portmap, false);
- tegra_xhci_hs_wake_on_interrupts(tegra->bdata->portmap, false);
-
/* enable_irq_wake for ss ports */
ret = enable_irq_wake(tegra->padctl_irq);
if (ret < 0) {
tegra_xhci_resume(struct platform_device *pdev)
{
struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
+ struct xhci_hcd *xhci = tegra->xhci;
dev_dbg(&pdev->dev, "%s\n", __func__);
+ mutex_lock(&tegra->sync_lock);
+ if (!tegra->init_done) {
+ xhci_warn(xhci, "%s: xhci probe not done\n",
+ __func__);
+ mutex_unlock(&tegra->sync_lock);
+ return -EBUSY;
+ }
+ mutex_unlock(&tegra->sync_lock);
+
tegra->last_jiffies = jiffies;
disable_irq_wake(tegra->padctl_irq);
}
#endif
+static int init_filesystem_firmware(struct tegra_xhci_hcd *tegra)
+{
+ struct platform_device *pdev = tegra->pdev;
+ int ret;
+
+ ret = request_firmware_nowait(THIS_MODULE, true, firmware_file,
+ &pdev->dev, GFP_KERNEL, tegra, init_filesystem_firmware_done);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "request_firmware failed %d\n", ret);
+ return ret;
+ }
-static int init_bootloader_firmware(struct tegra_xhci_hcd *tegra)
+ return ret;
+}
+
+static void init_filesystem_firmware_done(const struct firmware *fw,
+ void *context)
{
+ struct tegra_xhci_hcd *tegra = context;
struct platform_device *pdev = tegra->pdev;
- void __iomem *fw_mmio_base;
- phys_addr_t fw_mem_phy_addr;
+ struct cfgtbl *fw_cfgtbl;
size_t fw_size;
+ void *fw_data;
dma_addr_t fw_dma;
-#ifdef CONFIG_PLATFORM_ENABLE_IOMMU
int ret;
- DEFINE_DMA_ATTRS(attrs);
-#endif
-
- /* bootloader saved firmware memory address in PMC SCRATCH34 register */
- fw_mem_phy_addr = tegra_usb_pmc_reg_read(PMC_SCRATCH34);
- fw_mmio_base = devm_ioremap_nocache(&pdev->dev,
- fw_mem_phy_addr, sizeof(struct cfgtbl));
+ mutex_lock(&tegra->sync_lock);
- if (!fw_mmio_base) {
- dev_err(&pdev->dev, "error mapping fw memory 0x%x\n",
- fw_mem_phy_addr);
- return -ENOMEM;
+ if (fw == NULL) {
+ dev_err(&pdev->dev,
+ "failed to init firmware from filesystem: %s\n",
+ firmware_file);
+ goto err_firmware_done;
}
- fw_size = ioread32(fw_mmio_base + FW_SIZE_OFFSET);
- devm_iounmap(&pdev->dev, fw_mmio_base);
+ fw_cfgtbl = (struct cfgtbl *) fw->data;
+ fw_size = fw_cfgtbl->fwimg_len;
+ dev_info(&pdev->dev, "Firmware File: %s (%d Bytes)\n",
+ firmware_file, fw_size);
- fw_mmio_base = devm_ioremap_nocache(&pdev->dev,
- fw_mem_phy_addr, fw_size);
- if (!fw_mmio_base) {
- dev_err(&pdev->dev, "error mapping fw memory 0x%x\n",
- fw_mem_phy_addr);
- return -ENOMEM;
+ fw_data = dma_alloc_coherent(&pdev->dev, fw_size,
+ &fw_dma, GFP_KERNEL);
+ if (!fw_data) {
+ dev_err(&pdev->dev, "%s: dma_alloc_coherent failed\n",
+ __func__);
+ goto err_firmware_done;
}
- dev_info(&pdev->dev, "Firmware Memory: phy 0x%x mapped 0x%p (%d Bytes)\n",
- fw_mem_phy_addr, fw_mmio_base, fw_size);
-
-#ifdef CONFIG_PLATFORM_ENABLE_IOMMU
- dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
- fw_dma = dma_map_linear_attrs(&pdev->dev, fw_mem_phy_addr, fw_size,
- DMA_TO_DEVICE, &attrs);
-
- if (fw_dma == DMA_ERROR_CODE) {
- dev_err(&pdev->dev, "%s: dma_map_linear failed\n",
- __func__);
- ret = -ENOMEM;
- goto error_iounmap;
- }
-#else
- fw_dma = fw_mem_phy_addr;
-#endif
- dev_info(&pdev->dev, "Firmware DMA Memory: dma 0x%p (%d Bytes)\n",
- (void *) fw_dma, fw_size);
+ memcpy(fw_data, fw->data, fw_size);
+ dev_info(&pdev->dev,
+ "Firmware DMA Memory: dma 0x%p mapped 0x%p (%d Bytes)\n",
+ (void *) fw_dma, fw_data, fw_size);
/* all set and ready to go */
- tegra->firmware.data = fw_mmio_base;
+ tegra->firmware.data = fw_data;
tegra->firmware.dma = fw_dma;
tegra->firmware.size = fw_size;
- return 0;
+ ret = tegra_xhci_probe2(tegra);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s: failed to probe: %d\n", __func__, ret);
+ goto err_firmware_done;
+ }
-#ifdef CONFIG_PLATFORM_ENABLE_IOMMU
-error_iounmap:
- devm_iounmap(&pdev->dev, fw_mmio_base);
- return ret;
-#endif
+ release_firmware(fw);
+ mutex_unlock(&tegra->sync_lock);
+ return;
+
+err_firmware_done:
+ release_firmware(fw);
+ mutex_unlock(&tegra->sync_lock);
+ device_release_driver(&pdev->dev);
}
-static void deinit_bootloader_firmware(struct tegra_xhci_hcd *tegra)
+static void deinit_filesystem_firmware(struct tegra_xhci_hcd *tegra)
{
struct platform_device *pdev = tegra->pdev;
- void __iomem *fw_mmio_base = tegra->firmware.data;
-#ifdef CONFIG_PLATFORM_ENABLE_IOMMU
- dma_unmap_single(&pdev->dev, tegra->firmware.dma,
- tegra->firmware.size, DMA_TO_DEVICE);
-#endif
- devm_iounmap(&pdev->dev, fw_mmio_base);
+ if (tegra->firmware.data) {
+ dma_free_coherent(&pdev->dev, tegra->firmware.size,
+ tegra->firmware.data, tegra->firmware.dma);
+ }
memset(&tegra->firmware, 0, sizeof(tegra->firmware));
}
-
static int init_firmware(struct tegra_xhci_hcd *tegra)
{
- return init_bootloader_firmware(tegra);
+ return init_filesystem_firmware(tegra);
}
static void deinit_firmware(struct tegra_xhci_hcd *tegra)
{
- deinit_bootloader_firmware(tegra);
+ return deinit_filesystem_firmware(tegra);
}
static int tegra_enable_xusb_clk(struct tegra_xhci_hcd *tegra,
clk_disable(tegra->host_clk);
enable_host_clk_failed:
- if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
+ if (tegra->soc_config->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_disable(tegra->pll_re_vco_clk);
return err;
}
return NOTIFY_OK;
}
+static void tegra_xusb_read_board_data(struct tegra_xhci_hcd *tegra)
+{
+ struct tegra_xusb_board_data *bdata = tegra->bdata;
+ struct device_node *node = tegra->pdev->dev.of_node;
+ int ret;
+
+ bdata->uses_external_pmic = of_property_read_bool(node,
+ "nvidia,uses_external_pmic");
+ bdata->gpio_controls_muxed_ss_lanes = of_property_read_bool(node,
+ "nvidia,gpio_controls_muxed_ss_lanes");
+ ret = of_property_read_u32(node, "nvidia,gpio_ss1_sata",
+ &bdata->gpio_ss1_sata);
+ ret = of_property_read_u32(node, "nvidia,portmap",
+ &bdata->portmap);
+ ret = of_property_read_u32(node, "nvidia,ss_portmap",
+ (u32 *) &bdata->ss_portmap);
+ ret = of_property_read_u32(node, "nvidia,lane_owner",
+ (u32 *) &bdata->lane_owner);
+ ret = of_property_read_u32(node, "nvidia,ulpicap",
+ (u32 *) &bdata->ulpicap);
+ ret = of_property_read_u8_array(node, "nvidia,hsic0",
+ (u8 *) &bdata->hsic[0],
+ sizeof(bdata->hsic[0]));
+ ret = of_property_read_u8_array(node, "nvidia,hsic1",
+ (u8 *) &bdata->hsic[1],
+ sizeof(bdata->hsic[0]));
+ /* TODO: Add error conditions check */
+}
+
+static void tegra_xusb_read_calib_data(struct tegra_xhci_hcd *tegra)
+{
+ u32 usb_calib0 = tegra_fuse_readl(FUSE_SKU_USB_CALIB_0);
+ struct tegra_xusb_chip_calib *cdata = tegra->cdata;
+
+ pr_info("tegra_xusb_read_usb_calib: usb_calib0 = 0x%08x\n", usb_calib0);
+ /*
+ * read from usb_calib0 and pass to driver
+ * set HS_CURR_LEVEL (PAD0) = usb_calib0[5:0]
+ * set TERM_RANGE_ADJ = usb_calib0[10:7]
+ * set HS_SQUELCH_LEVEL = usb_calib0[12:11]
+ * set HS_IREF_CAP = usb_calib0[14:13]
+ * set HS_CURR_LEVEL (PAD1) = usb_calib0[20:15]
+ */
+
+ cdata->hs_curr_level_pad0 = (usb_calib0 >> 0) & 0x3f;
+ cdata->hs_term_range_adj = (usb_calib0 >> 7) & 0xf;
+ cdata->hs_squelch_level = (usb_calib0 >> 11) & 0x3;
+ cdata->hs_iref_cap = (usb_calib0 >> 13) & 0x3;
+ cdata->hs_curr_level_pad1 = (usb_calib0 >> 15) & 0x3f;
+ cdata->hs_curr_level_pad2 = (usb_calib0 >> 15) & 0x3f;
+}
+
+static const struct tegra_xusb_soc_config tegra114_soc_config = {
+ .pmc_portmap = (TEGRA_XUSB_UTMIP_PMC_PORT0 << 0) |
+ (TEGRA_XUSB_UTMIP_PMC_PORT2 << 4),
+ .quirks = TEGRA_XUSB_USE_HS_SRC_CLOCK2,
+ .rx_wander = (0x3 << 4),
+ .rx_eq = (0x3928 << 8),
+ .cdr_cntl = (0x26 << 24),
+ .dfe_cntl = 0x002008EE,
+ .hs_slew = (0xE << 6),
+ .ls_rslew_pad0 = (0x3 << 14),
+ .ls_rslew_pad1 = (0x0 << 14),
+ .hs_disc_lvl = (0x7 << 2),
+ .spare_in = 0x0,
+ .supply = {
+ .utmi_vbuses = {"usb_vbus0", "usb_vbus1", "usb_vbus2",},
+ .s3p3v = "hvdd_usb",
+ .s1p8v = "avdd_usb_pll",
+ .vddio_hsic = "vddio_hsic",
+ .s1p05v = "avddio_usb",
+ },
+};
+
+static const struct tegra_xusb_soc_config tegra124_soc_config = {
+ .pmc_portmap = (TEGRA_XUSB_UTMIP_PMC_PORT0 << 0) |
+ (TEGRA_XUSB_UTMIP_PMC_PORT1 << 4) |
+ (TEGRA_XUSB_UTMIP_PMC_PORT2 << 8),
+ .rx_wander = (0xF << 4),
+ .rx_eq = (0xF070 << 8),
+ .cdr_cntl = (0x26 << 24),
+ .dfe_cntl = 0x002008EE,
+ .hs_slew = (0xE << 6),
+ .ls_rslew_pad0 = (0x3 << 14),
+ .ls_rslew_pad1 = (0x0 << 14),
+ .ls_rslew_pad2 = (0x0 << 14),
+ .hs_disc_lvl = (0x7 << 2),
+ .spare_in = 0x1,
+ .supply = {
+ .utmi_vbuses = {"usb_vbus0", "usb_vbus1", "usb_vbus2",},
+ .s3p3v = "hvdd_usb",
+ .s1p8v = "avdd_pll_utmip",
+ .vddio_hsic = "vddio_hsic",
+ .s1p05v = "avddio_usb",
+ },
+};
+
+static struct of_device_id tegra_xhci_of_match[] = {
+ { .compatible = "nvidia,tegra114-xhci", .data = &tegra114_soc_config },
+ { .compatible = "nvidia,tegra124-xhci", .data = &tegra124_soc_config },
+ { },
+};
+
+static ssize_t hsic_power_show(struct device *dev,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
+ int pad;
+
+ for_each_enabled_hsic_pad(pad, tegra) {
+ if (&tegra->hsic_power_attr[pad] == attr)
+ return sprintf(buf, "%d\n", pad);
+ }
+
+ return sprintf(buf, "-1\n");
+}
+
+static ssize_t hsic_power_store(struct device *dev,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
+ enum MBOX_CMD_TYPE msg;
+ unsigned int on;
+ int pad, port;
+ int ret;
+
+ if (sscanf(buf, "%u", &on) != 1)
+ return -EINVAL;
+
+ if (on)
+ msg = MBOX_CMD_AIRPLANE_MODE_DISABLED;
+ else
+ msg = MBOX_CMD_AIRPLANE_MODE_ENABLED;
+
+ for_each_enabled_hsic_pad(pad, tegra) {
+ port = hsic_pad_to_port(pad);
+
+ if (&tegra->hsic_power_attr[pad] == attr) {
+ hsic_pad_pupd_set(tegra, pad, PUPD_IDLE);
+ ret = fw_message_send(tegra, msg, BIT(port + 1));
+ }
+ }
+
+ return n;
+}
+
+static void hsic_power_remove_file(struct tegra_xhci_hcd *tegra)
+{
+ struct device *dev = &tegra->pdev->dev;
+ int p;
+
+ for_each_enabled_hsic_pad(p, tegra) {
+ if (attr_name(tegra->hsic_power_attr[p])) {
+ device_remove_file(dev, &tegra->hsic_power_attr[p]);
+ kzfree(attr_name(tegra->hsic_power_attr[p]));
+ }
+ }
+
+}
+
+static int hsic_power_create_file(struct tegra_xhci_hcd *tegra)
+{
+ struct device *dev = &tegra->pdev->dev;
+ int p;
+ int err;
+
+ for_each_enabled_hsic_pad(p, tegra) {
+ attr_name(tegra->hsic_power_attr[p]) = kzalloc(16, GFP_KERNEL);
+ if (!attr_name(tegra->hsic_power_attr[p]))
+ return -ENOMEM;
+
+ snprintf(attr_name(tegra->hsic_power_attr[p]), 16,
+ "hsic%d_power", p);
+ tegra->hsic_power_attr[p].show = hsic_power_show;
+ tegra->hsic_power_attr[p].store = hsic_power_store;
+ tegra->hsic_power_attr[p].attr.mode = (S_IRUGO | S_IWUSR);
+ sysfs_attr_init(&tegra->hsic_power_attr[p].attr);
+
+ err = device_create_file(dev, &tegra->hsic_power_attr[p]);
+ if (err) {
+ kzfree(attr_name(tegra->hsic_power_attr[p]));
+ attr_name(tegra->hsic_power_attr[p]) = 0;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
/* TODO: we have to refine error handling in tegra_xhci_probe() */
static int tegra_xhci_probe(struct platform_device *pdev)
{
- const struct hc_driver *driver;
- struct xhci_hcd *xhci;
struct tegra_xhci_hcd *tegra;
struct resource *res;
- struct usb_hcd *hcd;
unsigned pad;
- unsigned port;
u32 val;
int ret;
int irq;
+ const struct tegra_xusb_soc_config *soc_config;
+ const struct of_device_id *match;
BUILD_BUG_ON(sizeof(struct cfgtbl) != 256);
dev_err(&pdev->dev, "memory alloc failed\n");
return -ENOMEM;
}
+ mutex_init(&tegra->sync_lock);
+ spin_lock_init(&tegra->lock);
+ mutex_init(&tegra->mbox_lock);
+
+ tegra->init_done = false;
+
+ tegra->bdata = devm_kzalloc(&pdev->dev, sizeof(
+ struct tegra_xusb_board_data),
+ GFP_KERNEL);
+ if (!tegra->bdata) {
+ dev_err(&pdev->dev, "memory alloc failed\n");
+ return -ENOMEM;
+ }
+ tegra->cdata = devm_kzalloc(&pdev->dev, sizeof(
+ struct tegra_xusb_chip_calib),
+ GFP_KERNEL);
+ if (!tegra->cdata) {
+ dev_err(&pdev->dev, "memory alloc failed\n");
+ return -ENOMEM;
+ }
+ match = of_match_device(tegra_xhci_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ soc_config = match->data;
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ tegra->tegra_xusb_dmamask = DMA_BIT_MASK(64);
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &tegra->tegra_xusb_dmamask;
+
tegra->pdev = pdev;
+ tegra_xusb_read_calib_data(tegra);
+ tegra_xusb_read_board_data(tegra);
tegra->pdata = dev_get_platdata(&pdev->dev);
- tegra->bdata = tegra->pdata->bdata;
+ tegra->bdata->portmap = tegra->pdata->portmap;
+ tegra->bdata->hsic[0].pretend_connect =
+ tegra->pdata->pretend_connect_0;
+ if (tegra->bdata->portmap == NULL)
+ return -ENODEV;
+ tegra->bdata->lane_owner = tegra->pdata->lane_owner;
+ tegra->soc_config = soc_config;
+ tegra->ss_pwr_gated = false;
+ tegra->host_pwr_gated = false;
+ tegra->hc_in_elpg = false;
+ tegra->hs_wake_event = false;
+ tegra->host_resume_req = false;
+ tegra->lp0_exit = false;
- ret = tegra_xhci_request_mem_region(pdev, "padctl",
- &tegra->padctl_base);
+ /* request resource padctl base address */
+ ret = tegra_xhci_request_mem_region(pdev, 3, &tegra->padctl_base);
if (ret) {
dev_err(&pdev->dev, "failed to map padctl\n");
return ret;
}
- ret = tegra_xhci_request_mem_region(pdev, "fpci", &tegra->fpci_base);
+ /* request resource fpci base address */
+ ret = tegra_xhci_request_mem_region(pdev, 1, &tegra->fpci_base);
if (ret) {
dev_err(&pdev->dev, "failed to map fpci\n");
return ret;
}
- ret = tegra_xhci_request_mem_region(pdev, "ipfs", &tegra->ipfs_base);
+ /* request resource ipfs base address */
+ ret = tegra_xhci_request_mem_region(pdev, 2, &tegra->ipfs_base);
if (ret) {
dev_err(&pdev->dev, "failed to map ipfs\n");
return ret;
if (IS_ERR_OR_NULL(tegra->transceiver)) {
dev_err(&pdev->dev, "failed to get usb phy\n");
tegra->transceiver = NULL;
- } else {
- otg_set_host(tegra->transceiver->otg, &hcd->self);
- tegra->otgnb.notifier_call = tegra_xhci_otg_notify;
- usb_register_notifier(tegra->transceiver,
- &tegra->otgnb);
}
}
+
/* Enable power rails to the PAD,VBUS
* and pull-up voltage.Initialize the regulators
*/
ret = tegra_xusb_regulator_init(tegra, pdev);
if (ret) {
dev_err(&pdev->dev, "failed to initialize xusb regulator\n");
+ if (ret == -ENODEV) {
+ ret = -EPROBE_DEFER;
+ dev_err(&pdev->dev, "Retry at a later stage\n");
+ }
goto err_deinit_xusb_partition_clk;
}
/* reset the pointer back to NULL. driver uses it */
/* platform_set_drvdata(pdev, NULL); */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "host");
+ /* request resource host base address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "mem resource host doesn't exist\n");
ret = -ENODEV;
goto err_deinit_usb2_clocks;
}
tegra->host_phy_base = res->start;
+ tegra->host_phy_size = resource_size(res);
tegra->host_phy_virt_base = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (XUSB_DEVICE_ID_T114 == tegra->device_id)
tegra_xhci_war_for_tctrl_rctrl(tegra);
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap)
- hsic_power_rail_enable(tegra);
- }
+ for_each_enabled_hsic_pad(pad, tegra)
+ hsic_power_rail_enable(tegra);
/* Program the XUSB pads to take ownership of ports */
tegra_xhci_padctl_portmap_and_caps(tegra);
tegra_periph_reset_deassert(tegra->host_clk);
tegra_periph_reset_deassert(tegra->ss_clk);
+ platform_set_drvdata(pdev, tegra);
fw_log_init(tegra);
ret = init_firmware(tegra);
if (ret < 0) {
dev_err(&pdev->dev, "failed to init firmware\n");
ret = -ENODEV;
- goto err_deinit_usb2_clocks;
+ goto err_deinit_firmware_log;
}
- ret = load_firmware(tegra, true /* do reset ARU */);
+ return 0;
+
+err_deinit_firmware_log:
+ fw_log_deinit(tegra);
+err_deinit_usb2_clocks:
+ tegra_usb2_clocks_deinit(tegra);
+err_deinit_tegra_xusb_regulator:
+ tegra_xusb_regulator_deinit(tegra);
+err_deinit_xusb_partition_clk:
+ if (tegra->transceiver)
+ usb_unregister_notifier(tegra->transceiver, &tegra->otgnb);
+
+ tegra_xusb_partitions_clk_deinit(tegra);
+
+ return ret;
+}
+
+static int tegra_xhci_probe2(struct tegra_xhci_hcd *tegra)
+{
+ struct platform_device *pdev = tegra->pdev;
+ const struct hc_driver *driver;
+ int ret;
+ struct resource *res;
+ int irq;
+ struct xhci_hcd *xhci;
+ struct usb_hcd *hcd;
+ unsigned port;
+
+
+ ret = load_firmware(tegra, false /* do reset ARU */);
if (ret < 0) {
dev_err(&pdev->dev, "failed to load firmware\n");
- ret = -ENODEV;
- goto err_deinit_firmware;
+ return -ENODEV;
}
-
- spin_lock_init(&tegra->lock);
- mutex_init(&tegra->sync_lock);
- mutex_init(&tegra->mbox_lock);
pmc_init(tegra);
device_init_wakeup(&pdev->dev, 1);
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "failed to create usb2 hcd\n");
- ret = -ENOMEM;
- goto err_deinit_firmware;
+ return -ENOMEM;
}
- ret = tegra_xhci_request_mem_region(pdev, "host", &hcd->regs);
+ /* request resource host base address */
+ ret = tegra_xhci_request_mem_region(pdev, 0, &hcd->regs);
if (ret) {
dev_err(&pdev->dev, "failed to map host\n");
goto err_put_usb2_hcd;
}
- hcd->rsrc_start = res->start;
- hcd->rsrc_len = resource_size(res);
+ hcd->rsrc_start = tegra->host_phy_base;
+ hcd->rsrc_len = tegra->host_phy_size;
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
+ /* Register interrupt handler for HOST */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "irq resource host doesn't exist\n");
ret = -ENODEV;
goto err_put_usb2_hcd;
}
+
irq = res->start;
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret) {
tegra->xhci = xhci;
platform_set_drvdata(pdev, tegra);
+ if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P0) {
+ if (!IS_ERR_OR_NULL(tegra->transceiver)) {
+ otg_set_host(tegra->transceiver->otg, &hcd->self);
+ tegra->otgnb.notifier_call = tegra_xhci_otg_notify;
+ usb_register_notifier(tegra->transceiver,
+ &tegra->otgnb);
+ }
+ }
+
xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
dev_name(&pdev->dev), hcd);
if (!xhci->shared_hcd) {
tegra->mbox_owner = 0xffff;
INIT_WORK(&tegra->mbox_work, tegra_xhci_process_mbox_message);
- tegra_xhci_enable_fw_message(tegra);
-
/* do ss partition elpg exit related initialization */
INIT_WORK(&tegra->ss_elpg_exit_work, ss_partition_elpg_exit_work);
/* Register interrupt handler for SMI line to handle mailbox
* interrupt from firmware
*/
- ret = tegra_xhci_request_irq(pdev, "host-smi", tegra_xhci_smi_irq,
+
+ ret = tegra_xhci_request_irq(pdev, 1, tegra_xhci_smi_irq,
IRQF_SHARED, "tegra_xhci_mbox_irq", &tegra->smi_irq);
if (ret != 0)
goto err_remove_usb3_hcd;
* handle wake on connect irqs interrupt from
* firmware
*/
- ret = tegra_xhci_request_irq(pdev, "padctl", tegra_xhci_padctl_irq,
+ ret = tegra_xhci_request_irq(pdev, 2, tegra_xhci_padctl_irq,
IRQF_SHARED | IRQF_TRIGGER_HIGH,
"tegra_xhci_padctl_irq", &tegra->padctl_irq);
if (ret != 0)
goto err_remove_usb3_hcd;
- ret = tegra_xhci_request_irq(pdev, "usb2", pmc_usb_phy_wake_isr,
+ /* Register interrupt wake handler for USB2 */
+ ret = tegra_xhci_request_irq(pdev, 4, pmc_usb_phy_wake_isr,
IRQF_SHARED | IRQF_TRIGGER_HIGH, "pmc_usb_phy_wake_isr",
&tegra->usb2_irq);
if (ret != 0)
goto err_remove_usb3_hcd;
- ret = tegra_xhci_request_irq(pdev, "usb3", pmc_usb_phy_wake_isr,
+ /* Register interrupt wake handler for USB3 */
+ ret = tegra_xhci_request_irq(pdev, 3, pmc_usb_phy_wake_isr,
IRQF_SHARED | IRQF_TRIGGER_HIGH, "pmc_usb_phy_wake_isr",
&tegra->usb3_irq);
if (ret != 0)
goto err_remove_usb3_hcd;
- tegra->ss_pwr_gated = false;
- tegra->host_pwr_gated = false;
- tegra->hc_in_elpg = false;
- tegra->hs_wake_event = false;
- tegra->host_resume_req = false;
- tegra->lp0_exit = false;
-
for (port = 0; port < XUSB_SS_PORT_COUNT; port++) {
tegra->ctle_ctx_saved[port] = false;
tegra->dfe_ctx_saved[port] = false;
}
+ tegra_xhci_enable_fw_message(tegra);
+ hsic_pad_pretend_connect(tegra);
+
tegra_xhci_debug_read_pads(tegra);
utmi_phy_pad_enable();
utmi_phy_iddq_override(false);
tegra_pd_add_device(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ hsic_power_create_file(tegra);
+ tegra->init_done = true;
return 0;
usb_remove_hcd(hcd);
err_put_usb2_hcd:
usb_put_hcd(hcd);
-err_deinit_firmware:
- deinit_firmware(tegra);
-err_deinit_usb2_clocks:
- tegra_usb2_clocks_deinit(tegra);
-err_deinit_tegra_xusb_regulator:
- tegra_xusb_regulator_deinit(tegra);
-err_deinit_xusb_partition_clk:
- usb_unregister_notifier(tegra->transceiver,
- &tegra->otgnb);
- tegra_xusb_partitions_clk_deinit(tegra);
return ret;
}
static int tegra_xhci_remove(struct platform_device *pdev)
{
struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
- struct xhci_hcd *xhci = NULL;
- struct usb_hcd *hcd = NULL;
unsigned pad;
if (tegra == NULL)
return -EINVAL;
- xhci = tegra->xhci;
- hcd = xhci_to_hcd(xhci);
+ mutex_lock(&tegra->sync_lock);
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap)
- hsic_pad_disable(tegra, pad);
+ for_each_enabled_hsic_pad(pad, tegra) {
+ hsic_pad_disable(tegra, pad);
+ hsic_power_rail_disable(tegra);
}
- for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
- if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap)
- hsic_power_rail_disable(tegra);
- }
+ if (tegra->init_done) {
+ struct xhci_hcd *xhci = NULL;
+ struct usb_hcd *hcd = NULL;
- devm_free_irq(&pdev->dev, tegra->usb3_irq, tegra);
- devm_free_irq(&pdev->dev, tegra->padctl_irq, tegra);
- devm_free_irq(&pdev->dev, tegra->smi_irq, tegra);
- usb_remove_hcd(xhci->shared_hcd);
- usb_put_hcd(xhci->shared_hcd);
- usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
- kfree(xhci);
+ xhci = tegra->xhci;
+ hcd = xhci_to_hcd(xhci);
+
+ devm_free_irq(&pdev->dev, tegra->usb3_irq, tegra);
+ devm_free_irq(&pdev->dev, tegra->padctl_irq, tegra);
+ devm_free_irq(&pdev->dev, tegra->smi_irq, tegra);
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_put_hcd(xhci->shared_hcd);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ kfree(xhci);
+ }
deinit_firmware(tegra);
fw_log_deinit(tegra);
+
tegra_xusb_regulator_deinit(tegra);
- usb_unregister_notifier(tegra->transceiver,
- &tegra->otgnb);
+
+ if (tegra->transceiver)
+ usb_unregister_notifier(tegra->transceiver, &tegra->otgnb);
+
tegra_usb2_clocks_deinit(tegra);
if (!tegra->hc_in_elpg)
tegra_xusb_partitions_clk_deinit(tegra);
+
utmi_phy_pad_disable();
utmi_phy_iddq_override(true);
tegra_pd_remove_device(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+
+ hsic_power_remove_file(tegra);
+ mutex_unlock(&tegra->sync_lock);
+
return 0;
}
#endif
.driver = {
.name = "tegra-xhci",
+ .of_match_table = tegra_xhci_of_match,
},
};
MODULE_ALIAS("platform:tegra-xhci");