From ea08eeccae9297f7aabd2ef7f0c2517ac4549acc Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 20 Feb 2024 01:18:26 +0000
Subject: [PATCH] write in 30M
---
kernel/drivers/usb/host/xhci-tegra.c | 1021 +++++++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 853 insertions(+), 168 deletions(-)
diff --git a/kernel/drivers/usb/host/xhci-tegra.c b/kernel/drivers/usb/host/xhci-tegra.c
index dc9cd8c..ffb0973 100644
--- a/kernel/drivers/usb/host/xhci-tegra.c
+++ b/kernel/drivers/usb/host/xhci-tegra.c
@@ -11,6 +11,7 @@
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -18,10 +19,14 @@
#include <linux/phy/tegra/xusb.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/role.h>
#include <soc/tegra/pmc.h>
#include "xhci.h"
@@ -37,23 +42,30 @@
#define XUSB_CFG_4 0x010
#define XUSB_BASE_ADDR_SHIFT 15
#define XUSB_BASE_ADDR_MASK 0x1ffff
+#define XUSB_CFG_16 0x040
+#define XUSB_CFG_24 0x060
+#define XUSB_CFG_AXI_CFG 0x0f8
#define XUSB_CFG_ARU_C11_CSBRANGE 0x41c
+#define XUSB_CFG_ARU_CONTEXT 0x43c
+#define XUSB_CFG_ARU_CONTEXT_HS_PLS 0x478
+#define XUSB_CFG_ARU_CONTEXT_FS_PLS 0x47c
+#define XUSB_CFG_ARU_CONTEXT_HSFS_SPEED 0x480
+#define XUSB_CFG_ARU_CONTEXT_HSFS_PP 0x484
#define XUSB_CFG_CSB_BASE_ADDR 0x800
/* FPCI mailbox registers */
-#define XUSB_CFG_ARU_MBOX_CMD 0x0e4
+/* XUSB_CFG_ARU_MBOX_CMD */
#define MBOX_DEST_FALC BIT(27)
#define MBOX_DEST_PME BIT(28)
#define MBOX_DEST_SMI BIT(29)
#define MBOX_DEST_XHCI BIT(30)
#define MBOX_INT_EN BIT(31)
-#define XUSB_CFG_ARU_MBOX_DATA_IN 0x0e8
+/* XUSB_CFG_ARU_MBOX_DATA_IN and XUSB_CFG_ARU_MBOX_DATA_OUT */
#define CMD_DATA_SHIFT 0
#define CMD_DATA_MASK 0xffffff
#define CMD_TYPE_SHIFT 24
#define CMD_TYPE_MASK 0xff
-#define XUSB_CFG_ARU_MBOX_DATA_OUT 0x0ec
-#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0
+/* XUSB_CFG_ARU_MBOX_OWNER */
#define MBOX_OWNER_NONE 0
#define MBOX_OWNER_FW 1
#define MBOX_OWNER_SW 2
@@ -62,11 +74,20 @@
#define MBOX_SMI_INTR_EN BIT(3)
/* IPFS registers */
+#define IPFS_XUSB_HOST_MSI_BAR_SZ_0 0x0c0
+#define IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0 0x0c4
+#define IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0 0x0c8
+#define IPFS_XUSB_HOST_MSI_VEC0_0 0x100
+#define IPFS_XUSB_HOST_MSI_EN_VEC0_0 0x140
#define IPFS_XUSB_HOST_CONFIGURATION_0 0x180
#define IPFS_EN_FPCI BIT(0)
+#define IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0 0x184
#define IPFS_XUSB_HOST_INTR_MASK_0 0x188
#define IPFS_IP_INT_MASK BIT(16)
+#define IPFS_XUSB_HOST_INTR_ENABLE_0 0x198
+#define IPFS_XUSB_HOST_UFPCI_CONFIG_0 0x19c
#define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc
+#define IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0 0x1dc
#define CSB_PAGE_SELECT_MASK 0x7fffff
#define CSB_PAGE_SELECT_SHIFT 9
@@ -101,41 +122,43 @@
#define L2IMEMOP_ACTION_SHIFT 24
#define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT)
#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT)
+#define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101a18
+#define L2IMEMOP_RESULT_VLD BIT(31)
#define XUSB_CSB_MP_APMAP 0x10181c
#define APMAP_BOOTPATH BIT(31)
#define IMEM_BLOCK_SIZE 256
struct tegra_xusb_fw_header {
- u32 boot_loadaddr_in_imem;
- u32 boot_codedfi_offset;
- u32 boot_codetag;
- u32 boot_codesize;
- u32 phys_memaddr;
- u16 reqphys_memsize;
- u16 alloc_phys_memsize;
- u32 rodata_img_offset;
- u32 rodata_section_start;
- u32 rodata_section_end;
- u32 main_fnaddr;
- u32 fwimg_cksum;
- u32 fwimg_created_time;
- u32 imem_resident_start;
- u32 imem_resident_end;
- u32 idirect_start;
- u32 idirect_end;
- u32 l2_imem_start;
- u32 l2_imem_end;
- u32 version_id;
+ __le32 boot_loadaddr_in_imem;
+ __le32 boot_codedfi_offset;
+ __le32 boot_codetag;
+ __le32 boot_codesize;
+ __le32 phys_memaddr;
+ __le16 reqphys_memsize;
+ __le16 alloc_phys_memsize;
+ __le32 rodata_img_offset;
+ __le32 rodata_section_start;
+ __le32 rodata_section_end;
+ __le32 main_fnaddr;
+ __le32 fwimg_cksum;
+ __le32 fwimg_created_time;
+ __le32 imem_resident_start;
+ __le32 imem_resident_end;
+ __le32 idirect_start;
+ __le32 idirect_end;
+ __le32 l2_imem_start;
+ __le32 l2_imem_end;
+ __le32 version_id;
u8 init_ddirect;
u8 reserved[3];
- u32 phys_addr_log_buffer;
- u32 total_log_entries;
- u32 dequeue_ptr;
- u32 dummy_var[2];
- u32 fwimg_len;
+ __le32 phys_addr_log_buffer;
+ __le32 total_log_entries;
+ __le32 dequeue_ptr;
+ __le32 dummy_var[2];
+ __le32 fwimg_len;
u8 magic[8];
- u32 ss_low_power_entry_timeout;
+ __le32 ss_low_power_entry_timeout;
u8 num_hsic_port;
u8 padding[139]; /* Pad to 256 bytes */
};
@@ -145,12 +168,32 @@
unsigned int num;
};
+struct tegra_xusb_mbox_regs {
+ u16 cmd;
+ u16 data_in;
+ u16 data_out;
+ u16 owner;
+};
+
+struct tegra_xusb_context_soc {
+ struct {
+ const unsigned int *offsets;
+ unsigned int num_offsets;
+ } ipfs;
+
+ struct {
+ const unsigned int *offsets;
+ unsigned int num_offsets;
+ } fpci;
+};
+
struct tegra_xusb_soc {
const char *firmware;
const char * const *supply_names;
unsigned int num_supplies;
const struct tegra_xusb_phy_type *phy_types;
unsigned int num_types;
+ const struct tegra_xusb_context_soc *context;
struct {
struct {
@@ -159,7 +202,17 @@
} usb2, ulpi, hsic, usb3;
} ports;
+ struct tegra_xusb_mbox_regs mbox;
+
bool scale_ss_clock;
+ bool has_ipfs;
+ bool lpm_support;
+ bool otg_reset_sspi;
+};
+
+struct tegra_xusb_context {
+ u32 *ipfs;
+ u32 *fpci;
};
struct tegra_xusb {
@@ -194,8 +247,21 @@
struct reset_control *host_rst;
struct reset_control *ss_rst;
+ struct device *genpd_dev_host;
+ struct device *genpd_dev_ss;
+ struct device_link *genpd_dl_host;
+ struct device_link *genpd_dl_ss;
+
struct phy **phys;
unsigned int num_phys;
+
+ struct usb_phy **usbphy;
+ unsigned int num_usb_phys;
+ int otg_usb2_port;
+ int otg_usb3_port;
+ bool host_mode;
+ struct notifier_block id_nb;
+ struct work_struct id_work;
/* Firmware loading related */
struct {
@@ -203,6 +269,8 @@
void *virt;
dma_addr_t phys;
} fw;
+
+ struct tegra_xusb_context context;
};
static struct hc_driver __read_mostly tegra_xhci_hc_driver;
@@ -347,29 +415,6 @@
MBOX_CMD_NAK
};
-static const char * const mbox_cmd_name[] = {
- [ 1] = "MSG_ENABLE",
- [ 2] = "INC_FALCON_CLOCK",
- [ 3] = "DEC_FALCON_CLOCK",
- [ 4] = "INC_SSPI_CLOCK",
- [ 5] = "DEC_SSPI_CLOCK",
- [ 6] = "SET_BW",
- [ 7] = "SET_SS_PWR_GATING",
- [ 8] = "SET_SS_PWR_UNGATING",
- [ 9] = "SAVE_DFE_CTLE_CTX",
- [ 10] = "AIRPLANE_MODE_ENABLED",
- [ 11] = "AIRPLANE_MODE_DISABLED",
- [ 12] = "START_HSIC_IDLE",
- [ 13] = "STOP_HSIC_IDLE",
- [ 14] = "DBC_WAKE_STACK",
- [ 15] = "HSIC_PRETEND_CONNECT",
- [ 16] = "RESET_SSPI",
- [ 17] = "DISABLE_SS_LFPS_DETECTION",
- [ 18] = "ENABLE_SS_LFPS_DETECTION",
- [128] = "ACK",
- [129] = "NAK",
-};
-
struct tegra_xusb_mbox_msg {
u32 cmd;
u32 data;
@@ -411,15 +456,15 @@
* ACK/NAK messages.
*/
if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) {
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value != MBOX_OWNER_NONE) {
dev_err(tegra->dev, "mailbox is busy\n");
return -EBUSY;
}
- fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER);
+ fpci_writel(tegra, MBOX_OWNER_SW, tegra->soc->mbox.owner);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value != MBOX_OWNER_SW) {
dev_err(tegra->dev, "failed to acquire mailbox\n");
return -EBUSY;
@@ -429,17 +474,17 @@
}
value = tegra_xusb_mbox_pack(msg);
- fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN);
+ fpci_writel(tegra, value, tegra->soc->mbox.data_in);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
+ value = fpci_readl(tegra, tegra->soc->mbox.cmd);
value |= MBOX_INT_EN | MBOX_DEST_FALC;
- fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
+ fpci_writel(tegra, value, tegra->soc->mbox.cmd);
if (wait_for_idle) {
unsigned long timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value == MBOX_OWNER_NONE)
break;
@@ -447,7 +492,7 @@
}
if (time_after(jiffies, timeout))
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value != MBOX_OWNER_NONE)
return -ETIMEDOUT;
@@ -621,16 +666,16 @@
mutex_lock(&tegra->lock);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT);
+ value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(&msg, value);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
+ value = fpci_readl(tegra, tegra->soc->mbox.cmd);
value &= ~MBOX_DEST_SMI;
- fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
+ fpci_writel(tegra, value, tegra->soc->mbox.cmd);
/* clear mailbox owner if no ACK/NAK is required */
if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd))
- fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER);
+ fpci_writel(tegra, MBOX_OWNER_NONE, tegra->soc->mbox.owner);
tegra_xusb_mbox_handle(tegra, &msg);
@@ -638,21 +683,23 @@
return IRQ_HANDLED;
}
-static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra,
- struct resource *regs)
+static void tegra_xusb_config(struct tegra_xusb *tegra)
{
+ u32 regs = tegra->hcd->rsrc_start;
u32 value;
- value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0);
- value |= IPFS_EN_FPCI;
- ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0);
+ if (tegra->soc->has_ipfs) {
+ value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0);
+ value |= IPFS_EN_FPCI;
+ ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0);
- usleep_range(10, 20);
+ usleep_range(10, 20);
+ }
/* Program BAR0 space */
value = fpci_readl(tegra, XUSB_CFG_4);
value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
- value |= regs->start & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
+ value |= regs & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
fpci_writel(tegra, value, XUSB_CFG_4);
usleep_range(100, 200);
@@ -662,13 +709,15 @@
value |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN;
fpci_writel(tegra, value, XUSB_CFG_1);
- /* Enable interrupt assertion */
- value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0);
- value |= IPFS_IP_INT_MASK;
- ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0);
+ if (tegra->soc->has_ipfs) {
+ /* Enable interrupt assertion */
+ value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0);
+ value |= IPFS_IP_INT_MASK;
+ ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0);
- /* Set hysteresis */
- ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
+ /* Set hysteresis */
+ ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
+ }
}
static int tegra_xusb_clk_enable(struct tegra_xusb *tegra)
@@ -774,7 +823,6 @@
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- tegra_xusb_phy_disable(tegra);
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
tegra_xusb_clk_disable(tegra);
@@ -798,32 +846,41 @@
goto disable_clk;
}
- err = tegra_xusb_phy_enable(tegra);
- if (err < 0) {
- dev_err(dev, "failed to enable PHYs: %d\n", err);
- goto disable_regulator;
- }
-
return 0;
-disable_regulator:
- regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
disable_clk:
tegra_xusb_clk_disable(tegra);
return err;
}
-static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
+#ifdef CONFIG_PM_SLEEP
+static int tegra_xusb_init_context(struct tegra_xusb *tegra)
{
- unsigned int code_tag_blocks, code_size_blocks, code_blocks;
+ const struct tegra_xusb_context_soc *soc = tegra->soc->context;
+
+ tegra->context.ipfs = devm_kcalloc(tegra->dev, soc->ipfs.num_offsets,
+ sizeof(u32), GFP_KERNEL);
+ if (!tegra->context.ipfs)
+ return -ENOMEM;
+
+ tegra->context.fpci = devm_kcalloc(tegra->dev, soc->fpci.num_offsets,
+ sizeof(u32), GFP_KERNEL);
+ if (!tegra->context.fpci)
+ return -ENOMEM;
+
+ return 0;
+}
+#else
+static inline int tegra_xusb_init_context(struct tegra_xusb *tegra)
+{
+ return 0;
+}
+#endif
+
+static int tegra_xusb_request_firmware(struct tegra_xusb *tegra)
+{
struct tegra_xusb_fw_header *header;
- struct device *dev = tegra->dev;
const struct firmware *fw;
- unsigned long timeout;
- time64_t timestamp;
- struct tm time;
- u64 address;
- u32 value;
int err;
err = request_firmware(&fw, tegra->soc->firmware, tegra->dev);
@@ -847,6 +904,26 @@
header = (struct tegra_xusb_fw_header *)tegra->fw.virt;
memcpy(tegra->fw.virt, fw->data, tegra->fw.size);
release_firmware(fw);
+
+ return 0;
+}
+
+static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
+{
+ unsigned int code_tag_blocks, code_size_blocks, code_blocks;
+ struct xhci_cap_regs __iomem *cap = tegra->regs;
+ struct tegra_xusb_fw_header *header;
+ struct device *dev = tegra->dev;
+ struct xhci_op_regs __iomem *op;
+ unsigned long timeout;
+ time64_t timestamp;
+ struct tm time;
+ u64 address;
+ u32 value;
+ int err;
+
+ header = (struct tegra_xusb_fw_header *)tegra->fw.virt;
+ op = tegra->regs + HC_LENGTH(readl(&cap->hc_capbase));
if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
dev_info(dev, "Firmware already loaded, Falcon state %#x\n",
@@ -902,26 +979,37 @@
csb_writel(tegra, 0, XUSB_FALC_DMACTL);
- msleep(50);
+ /* wait for RESULT_VLD to get set */
+#define tegra_csb_readl(offset) csb_readl(tegra, offset)
+ err = readx_poll_timeout(tegra_csb_readl,
+ XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT, value,
+ value & L2IMEMOP_RESULT_VLD, 100, 10000);
+ if (err < 0) {
+ dev_err(dev, "DMA controller not ready %#010x\n", value);
+ return err;
+ }
+#undef tegra_csb_readl
csb_writel(tegra, le32_to_cpu(header->boot_codetag),
XUSB_FALC_BOOTVEC);
- /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */
- timeout = jiffies + msecs_to_jiffies(5);
-
+ /* Boot Falcon CPU and wait for USBSTS_CNR to get cleared. */
csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
- while (time_before(jiffies, timeout)) {
- if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED)
+ timeout = jiffies + msecs_to_jiffies(200);
+
+ do {
+ value = readl(&op->status);
+ if ((value & STS_CNR) == 0)
break;
- usleep_range(100, 200);
- }
+ usleep_range(1000, 2000);
+ } while (time_is_after_jiffies(timeout));
- if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) {
- dev_err(dev, "Falcon failed to start, state: %#x\n",
- csb_readl(tegra, XUSB_FALC_CPUCTL));
+ value = readl(&op->status);
+ if (value & STS_CNR) {
+ value = csb_readl(tegra, XUSB_FALC_CPUCTL);
+ dev_err(dev, "XHCI controller not read: %#010x\n", value);
return -EIO;
}
@@ -935,11 +1023,289 @@
return 0;
}
-static int tegra_xusb_probe(struct platform_device *pdev)
+static void tegra_xusb_powerdomain_remove(struct device *dev,
+ struct tegra_xusb *tegra)
+{
+ if (tegra->genpd_dl_ss)
+ device_link_del(tegra->genpd_dl_ss);
+ if (tegra->genpd_dl_host)
+ device_link_del(tegra->genpd_dl_host);
+ if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
+ dev_pm_domain_detach(tegra->genpd_dev_ss, true);
+ if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
+ dev_pm_domain_detach(tegra->genpd_dev_host, true);
+}
+
+static int tegra_xusb_powerdomain_init(struct device *dev,
+ struct tegra_xusb *tegra)
+{
+ int err;
+
+ tegra->genpd_dev_host = dev_pm_domain_attach_by_name(dev, "xusb_host");
+ if (IS_ERR(tegra->genpd_dev_host)) {
+ err = PTR_ERR(tegra->genpd_dev_host);
+ dev_err(dev, "failed to get host pm-domain: %d\n", err);
+ return err;
+ }
+
+ tegra->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "xusb_ss");
+ if (IS_ERR(tegra->genpd_dev_ss)) {
+ err = PTR_ERR(tegra->genpd_dev_ss);
+ dev_err(dev, "failed to get superspeed pm-domain: %d\n", err);
+ return err;
+ }
+
+ tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!tegra->genpd_dl_host) {
+ dev_err(dev, "adding host device link failed!\n");
+ return -ENODEV;
+ }
+
+ tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!tegra->genpd_dl_ss) {
+ dev_err(dev, "adding superspeed device link failed!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
{
struct tegra_xusb_mbox_msg msg;
- struct resource *res, *regs;
+ int err;
+
+ /* Enable firmware messages from controller. */
+ msg.cmd = MBOX_CMD_MSG_ENABLED;
+ msg.data = 0;
+
+ err = tegra_xusb_mbox_send(tegra, &msg);
+ if (err < 0)
+ dev_err(tegra->dev, "failed to enable messages: %d\n", err);
+
+ return err;
+}
+
+static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
+{
+ int err;
+
+ mutex_lock(&tegra->lock);
+ err = __tegra_xusb_enable_firmware_messages(tegra);
+ mutex_unlock(&tegra->lock);
+
+ return err;
+}
+
+static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main,
+ bool set)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd;
+ unsigned int wait = (!main && !set) ? 1000 : 10;
+ u16 typeReq = set ? SetPortFeature : ClearPortFeature;
+ u16 wIndex = main ? tegra->otg_usb2_port + 1 : tegra->otg_usb3_port + 1;
+ u32 status;
+ u32 stat_power = main ? USB_PORT_STAT_POWER : USB_SS_PORT_STAT_POWER;
+ u32 status_val = set ? stat_power : 0;
+
+ dev_dbg(tegra->dev, "%s():%s %s port power\n", __func__,
+ set ? "set" : "clear", main ? "HS" : "SS");
+
+ hcd->driver->hub_control(hcd, typeReq, USB_PORT_FEAT_POWER, wIndex,
+ NULL, 0);
+
+ do {
+ tegra_xhci_hc_driver.hub_control(hcd, GetPortStatus, 0, wIndex,
+ (char *) &status, sizeof(status));
+ if (status_val == (status & stat_power))
+ break;
+
+ if (!main && !set)
+ usleep_range(600, 700);
+ else
+ usleep_range(10, 20);
+ } while (--wait > 0);
+
+ if (status_val != (status & stat_power))
+ dev_info(tegra->dev, "failed to %s %s PP %d\n",
+ set ? "set" : "clear",
+ main ? "HS" : "SS", status);
+}
+
+static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name,
+ int port)
+{
+ unsigned int i, phy_count = 0;
+
+ for (i = 0; i < tegra->soc->num_types; i++) {
+ if (!strncmp(tegra->soc->phy_types[i].name, name,
+ strlen(name)))
+ return tegra->phys[phy_count+port];
+
+ phy_count += tegra->soc->phy_types[i].num;
+ }
+
+ return NULL;
+}
+
+static void tegra_xhci_id_work(struct work_struct *work)
+{
+ struct tegra_xusb *tegra = container_of(work, struct tegra_xusb,
+ id_work);
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct tegra_xusb_mbox_msg msg;
+ struct phy *phy = tegra_xusb_get_phy(tegra, "usb2",
+ tegra->otg_usb2_port);
+ u32 status;
+ int ret;
+
+ dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off");
+
+ mutex_lock(&tegra->lock);
+
+ if (tegra->host_mode)
+ phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
+ else
+ phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
+
+ mutex_unlock(&tegra->lock);
+
+ tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl,
+ tegra->otg_usb2_port);
+
+ if (tegra->host_mode) {
+ /* switch to host mode */
+ if (tegra->otg_usb3_port >= 0) {
+ if (tegra->soc->otg_reset_sspi) {
+ /* set PP=0 */
+ tegra_xhci_hc_driver.hub_control(
+ xhci->shared_hcd, GetPortStatus,
+ 0, tegra->otg_usb3_port+1,
+ (char *) &status, sizeof(status));
+ if (status & USB_SS_PORT_STAT_POWER)
+ tegra_xhci_set_port_power(tegra, false,
+ false);
+
+ /* reset OTG port SSPI */
+ msg.cmd = MBOX_CMD_RESET_SSPI;
+ msg.data = tegra->otg_usb3_port+1;
+
+ ret = tegra_xusb_mbox_send(tegra, &msg);
+ if (ret < 0) {
+ dev_info(tegra->dev,
+ "failed to RESET_SSPI %d\n",
+ ret);
+ }
+ }
+
+ tegra_xhci_set_port_power(tegra, false, true);
+ }
+
+ tegra_xhci_set_port_power(tegra, true, true);
+
+ } else {
+ if (tegra->otg_usb3_port >= 0)
+ tegra_xhci_set_port_power(tegra, false, false);
+
+ tegra_xhci_set_port_power(tegra, true, false);
+ }
+}
+
+static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra,
+ struct usb_phy *usbphy)
+{
+ unsigned int i;
+
+ for (i = 0; i < tegra->num_usb_phys; i++) {
+ if (tegra->usbphy[i] && usbphy == tegra->usbphy[i])
+ return i;
+ }
+
+ return -1;
+}
+
+static int tegra_xhci_id_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct tegra_xusb *tegra = container_of(nb, struct tegra_xusb,
+ id_nb);
+ struct usb_phy *usbphy = (struct usb_phy *)data;
+
+ dev_dbg(tegra->dev, "%s(): action is %d", __func__, usbphy->last_event);
+
+ if ((tegra->host_mode && usbphy->last_event == USB_EVENT_ID) ||
+ (!tegra->host_mode && usbphy->last_event != USB_EVENT_ID)) {
+ dev_dbg(tegra->dev, "Same role(%d) received. Ignore",
+ tegra->host_mode);
+ return NOTIFY_OK;
+ }
+
+ tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy);
+
+ tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false;
+
+ schedule_work(&tegra->id_work);
+
+ return NOTIFY_OK;
+}
+
+static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ tegra->usbphy = devm_kcalloc(tegra->dev, tegra->num_usb_phys,
+ sizeof(*tegra->usbphy), GFP_KERNEL);
+ if (!tegra->usbphy)
+ return -ENOMEM;
+
+ INIT_WORK(&tegra->id_work, tegra_xhci_id_work);
+ tegra->id_nb.notifier_call = tegra_xhci_id_notify;
+ tegra->otg_usb2_port = -EINVAL;
+ tegra->otg_usb3_port = -EINVAL;
+
+ for (i = 0; i < tegra->num_usb_phys; i++) {
+ struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i);
+
+ if (!phy)
+ continue;
+
+ tegra->usbphy[i] = devm_usb_get_phy_by_node(tegra->dev,
+ phy->dev.of_node,
+ &tegra->id_nb);
+ if (!IS_ERR(tegra->usbphy[i])) {
+ dev_dbg(tegra->dev, "usbphy-%d registered", i);
+ otg_set_host(tegra->usbphy[i]->otg, &tegra->hcd->self);
+ } else {
+ /*
+ * usb-phy is optional, continue if its not available.
+ */
+ tegra->usbphy[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ cancel_work_sync(&tegra->id_work);
+
+ for (i = 0; i < tegra->num_usb_phys; i++)
+ if (tegra->usbphy[i])
+ otg_set_host(tegra->usbphy[i]->otg, NULL);
+}
+
+static int tegra_xusb_probe(struct platform_device *pdev)
+{
struct tegra_xusb *tegra;
+ struct resource *regs;
struct xhci_hcd *xhci;
unsigned int i, j, k;
struct phy *phy;
@@ -955,20 +1321,24 @@
mutex_init(&tegra->lock);
tegra->dev = &pdev->dev;
+ err = tegra_xusb_init_context(tegra);
+ if (err < 0)
+ return err;
+
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tegra->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res);
+ tegra->fpci_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(tegra->fpci_base))
return PTR_ERR(tegra->fpci_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(tegra->ipfs_base))
- return PTR_ERR(tegra->ipfs_base);
+ if (tegra->soc->has_ipfs) {
+ tegra->ipfs_base = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(tegra->ipfs_base))
+ return PTR_ERR(tegra->ipfs_base);
+ }
tegra->xhci_irq = platform_get_irq(pdev, 0);
if (tegra->xhci_irq < 0)
@@ -1045,7 +1415,7 @@
goto put_padctl;
}
- if (!pdev->dev.pm_domain) {
+ if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
tegra->host_rst = devm_reset_control_get(&pdev->dev,
"xusb_host");
if (IS_ERR(tegra->host_rst)) {
@@ -1076,37 +1446,46 @@
tegra->host_clk,
tegra->host_rst);
if (err) {
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
dev_err(&pdev->dev,
"failed to enable XUSBC domain: %d\n", err);
- goto disable_xusba;
+ goto put_padctl;
}
+ } else {
+ err = tegra_xusb_powerdomain_init(&pdev->dev, tegra);
+ if (err)
+ goto put_powerdomains;
}
tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies,
sizeof(*tegra->supplies), GFP_KERNEL);
if (!tegra->supplies) {
err = -ENOMEM;
- goto disable_xusbc;
+ goto put_powerdomains;
}
- for (i = 0; i < tegra->soc->num_supplies; i++)
- tegra->supplies[i].supply = tegra->soc->supply_names[i];
+ regulator_bulk_set_supply_names(tegra->supplies,
+ tegra->soc->supply_names,
+ tegra->soc->num_supplies);
err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies,
tegra->supplies);
if (err) {
dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
- goto disable_xusbc;
+ goto put_powerdomains;
}
- for (i = 0; i < tegra->soc->num_types; i++)
+ for (i = 0; i < tegra->soc->num_types; i++) {
+ if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4))
+ tegra->num_usb_phys = tegra->soc->phy_types[i].num;
tegra->num_phys += tegra->soc->phy_types[i].num;
+ }
tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
sizeof(*tegra->phys), GFP_KERNEL);
if (!tegra->phys) {
err = -ENOMEM;
- goto disable_xusbc;
+ goto put_powerdomains;
}
for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
@@ -1122,7 +1501,7 @@
"failed to get PHY %s: %ld\n", prop,
PTR_ERR(phy));
err = PTR_ERR(phy);
- goto disable_xusbc;
+ goto put_powerdomains;
}
tegra->phys[k++] = phy;
@@ -1133,8 +1512,12 @@
dev_name(&pdev->dev));
if (!tegra->hcd) {
err = -ENOMEM;
- goto disable_xusbc;
+ goto put_powerdomains;
}
+
+ tegra->hcd->regs = tegra->regs;
+ tegra->hcd->rsrc_start = regs->start;
+ tegra->hcd->rsrc_len = resource_size(regs);
/*
* This must happen after usb_create_hcd(), because usb_create_hcd()
@@ -1142,18 +1525,11 @@
*/
platform_set_drvdata(pdev, tegra);
- pm_runtime_enable(&pdev->dev);
- if (pm_runtime_enabled(&pdev->dev))
- err = pm_runtime_get_sync(&pdev->dev);
- else
- err = tegra_xusb_runtime_resume(&pdev->dev);
-
+ err = tegra_xusb_phy_enable(tegra);
if (err < 0) {
- dev_err(&pdev->dev, "failed to enable device: %d\n", err);
- goto disable_rpm;
+ dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
+ goto put_hcd;
}
-
- tegra_xusb_ipfs_config(tegra, regs);
/*
* The XUSB Falcon microcontroller can only address 40 bits, so set
@@ -1162,18 +1538,34 @@
err = dma_set_mask_and_coherent(tegra->dev, DMA_BIT_MASK(40));
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
- goto put_rpm;
+ goto disable_phy;
}
+
+ err = tegra_xusb_request_firmware(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request firmware: %d\n", err);
+ goto disable_phy;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ if (!pm_runtime_enabled(&pdev->dev))
+ err = tegra_xusb_runtime_resume(&pdev->dev);
+ else
+ err = pm_runtime_get_sync(&pdev->dev);
+
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to enable device: %d\n", err);
+ goto free_firmware;
+ }
+
+ tegra_xusb_config(tegra);
err = tegra_xusb_load_firmware(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to load firmware: %d\n", err);
goto put_rpm;
}
-
- tegra->hcd->regs = tegra->regs;
- tegra->hcd->rsrc_start = regs->start;
- tegra->hcd->rsrc_len = resource_size(regs);
err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
@@ -1201,20 +1593,11 @@
goto put_usb3;
}
- mutex_lock(&tegra->lock);
-
- /* Enable firmware messages from controller. */
- msg.cmd = MBOX_CMD_MSG_ENABLED;
- msg.data = 0;
-
- err = tegra_xusb_mbox_send(tegra, &msg);
+ err = tegra_xusb_enable_firmware_messages(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable messages: %d\n", err);
- mutex_unlock(&tegra->lock);
goto remove_usb3;
}
-
- mutex_unlock(&tegra->lock);
err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq,
tegra_xusb_mbox_irq,
@@ -1222,6 +1605,12 @@
dev_name(&pdev->dev), tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+ goto remove_usb3;
+ }
+
+ err = tegra_xusb_init_usb_phy(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err);
goto remove_usb3;
}
@@ -1236,15 +1625,21 @@
put_rpm:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra_xusb_runtime_suspend(&pdev->dev);
-disable_rpm:
- pm_runtime_disable(&pdev->dev);
+put_hcd:
usb_put_hcd(tegra->hcd);
-disable_xusbc:
- if (!pdev->dev.pm_domain)
+free_firmware:
+ dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
+ tegra->fw.phys);
+disable_phy:
+ tegra_xusb_phy_disable(tegra);
+ pm_runtime_disable(&pdev->dev);
+put_powerdomains:
+ if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
-disable_xusba:
- if (!pdev->dev.pm_domain)
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ } else {
+ tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+ }
put_padctl:
tegra_xusb_padctl_put(tegra->padctl);
return err;
@@ -1254,6 +1649,8 @@
{
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+
+ tegra_xusb_deinit_usb_phy(tegra);
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
@@ -1267,28 +1664,191 @@
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ } else {
+ tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+ }
+
+ tegra_xusb_phy_disable(tegra);
+
tegra_xusb_padctl_put(tegra->padctl);
return 0;
}
#ifdef CONFIG_PM_SLEEP
+static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
+{
+ struct device *dev = hub->hcd->self.controller;
+ bool status = true;
+ unsigned int i;
+ u32 value;
+
+ for (i = 0; i < hub->num_ports; i++) {
+ value = readl(hub->ports[i]->addr);
+ if ((value & PORT_PE) == 0)
+ continue;
+
+ if ((value & PORT_PLS_MASK) != XDEV_U3) {
+ dev_info(dev, "%u-%u isn't suspended: %#010x\n",
+ hub->hcd->self.busnum, i + 1, value);
+ status = false;
+ }
+ }
+
+ return status;
+}
+
+static int tegra_xusb_check_ports(struct tegra_xusb *tegra)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) ||
+ !xhci_hub_ports_suspended(&xhci->usb3_rhub))
+ err = -EBUSY;
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ return err;
+}
+
+static void tegra_xusb_save_context(struct tegra_xusb *tegra)
+{
+ const struct tegra_xusb_context_soc *soc = tegra->soc->context;
+ struct tegra_xusb_context *ctx = &tegra->context;
+ unsigned int i;
+
+ if (soc->ipfs.num_offsets > 0) {
+ for (i = 0; i < soc->ipfs.num_offsets; i++)
+ ctx->ipfs[i] = ipfs_readl(tegra, soc->ipfs.offsets[i]);
+ }
+
+ if (soc->fpci.num_offsets > 0) {
+ for (i = 0; i < soc->fpci.num_offsets; i++)
+ ctx->fpci[i] = fpci_readl(tegra, soc->fpci.offsets[i]);
+ }
+}
+
+static void tegra_xusb_restore_context(struct tegra_xusb *tegra)
+{
+ const struct tegra_xusb_context_soc *soc = tegra->soc->context;
+ struct tegra_xusb_context *ctx = &tegra->context;
+ unsigned int i;
+
+ if (soc->fpci.num_offsets > 0) {
+ for (i = 0; i < soc->fpci.num_offsets; i++)
+ fpci_writel(tegra, ctx->fpci[i], soc->fpci.offsets[i]);
+ }
+
+ if (soc->ipfs.num_offsets > 0) {
+ for (i = 0; i < soc->ipfs.num_offsets; i++)
+ ipfs_writel(tegra, ctx->ipfs[i], soc->ipfs.offsets[i]);
+ }
+}
+
+static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ int err;
+
+ err = tegra_xusb_check_ports(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "not all ports suspended: %d\n", err);
+ return err;
+ }
+
+ err = xhci_suspend(xhci, wakeup);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err);
+ return err;
+ }
+
+ tegra_xusb_save_context(tegra);
+ tegra_xusb_phy_disable(tegra);
+ tegra_xusb_clk_disable(tegra);
+
+ return 0;
+}
+
+static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ int err;
+
+ err = tegra_xusb_clk_enable(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable clocks: %d\n", err);
+ return err;
+ }
+
+ err = tegra_xusb_phy_enable(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable PHYs: %d\n", err);
+ goto disable_clk;
+ }
+
+ tegra_xusb_config(tegra);
+ tegra_xusb_restore_context(tegra);
+
+ err = tegra_xusb_load_firmware(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to load firmware: %d\n", err);
+ goto disable_phy;
+ }
+
+ err = __tegra_xusb_enable_firmware_messages(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable messages: %d\n", err);
+ goto disable_phy;
+ }
+
+ err = xhci_resume(xhci, true);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
+ goto disable_phy;
+ }
+
+ return 0;
+
+disable_phy:
+ tegra_xusb_phy_disable(tegra);
+disable_clk:
+ tegra_xusb_clk_disable(tegra);
+ return err;
+}
+
static int tegra_xusb_suspend(struct device *dev)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
bool wakeup = device_may_wakeup(dev);
+ int err;
- /* TODO: Powergate controller across suspend/resume. */
- return xhci_suspend(xhci, wakeup);
+ synchronize_irq(tegra->mbox_irq);
+
+ mutex_lock(&tegra->lock);
+ err = tegra_xusb_enter_elpg(tegra, wakeup);
+ mutex_unlock(&tegra->lock);
+
+ return err;
}
static int tegra_xusb_resume(struct device *dev)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ bool wakeup = device_may_wakeup(dev);
+ int err;
- return xhci_resume(xhci, 0);
+ mutex_lock(&tegra->lock);
+ err = tegra_xusb_exit_elpg(tegra, wakeup);
+ mutex_unlock(&tegra->lock);
+
+ return err;
}
#endif
@@ -1302,11 +1862,7 @@
"avddio-pex",
"dvddio-pex",
"avdd-usb",
- "avdd-pll-utmip",
- "avdd-pll-erefe",
- "avdd-usb-ss-pll",
"hvdd-usb-ss",
- "hvdd-usb-ss-pll-e",
};
static const struct tegra_xusb_phy_type tegra124_phy_types[] = {
@@ -1315,18 +1871,63 @@
{ .name = "hsic", .num = 2, },
};
+static const unsigned int tegra124_xusb_context_ipfs[] = {
+ IPFS_XUSB_HOST_MSI_BAR_SZ_0,
+ IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0,
+ IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0,
+ IPFS_XUSB_HOST_MSI_VEC0_0,
+ IPFS_XUSB_HOST_MSI_EN_VEC0_0,
+ IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0,
+ IPFS_XUSB_HOST_INTR_MASK_0,
+ IPFS_XUSB_HOST_INTR_ENABLE_0,
+ IPFS_XUSB_HOST_UFPCI_CONFIG_0,
+ IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0,
+ IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0,
+};
+
+static const unsigned int tegra124_xusb_context_fpci[] = {
+ XUSB_CFG_ARU_CONTEXT_HS_PLS,
+ XUSB_CFG_ARU_CONTEXT_FS_PLS,
+ XUSB_CFG_ARU_CONTEXT_HSFS_SPEED,
+ XUSB_CFG_ARU_CONTEXT_HSFS_PP,
+ XUSB_CFG_ARU_CONTEXT,
+ XUSB_CFG_AXI_CFG,
+ XUSB_CFG_24,
+ XUSB_CFG_16,
+};
+
+static const struct tegra_xusb_context_soc tegra124_xusb_context = {
+ .ipfs = {
+ .num_offsets = ARRAY_SIZE(tegra124_xusb_context_ipfs),
+ .offsets = tegra124_xusb_context_ipfs,
+ },
+ .fpci = {
+ .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci),
+ .offsets = tegra124_xusb_context_fpci,
+ },
+};
+
static const struct tegra_xusb_soc tegra124_soc = {
.firmware = "nvidia/tegra124/xusb.bin",
.supply_names = tegra124_supply_names,
.num_supplies = ARRAY_SIZE(tegra124_supply_names),
.phy_types = tegra124_phy_types,
.num_types = ARRAY_SIZE(tegra124_phy_types),
+ .context = &tegra124_xusb_context,
.ports = {
.usb2 = { .offset = 4, .count = 4, },
.hsic = { .offset = 6, .count = 2, },
.usb3 = { .offset = 0, .count = 2, },
},
.scale_ss_clock = true,
+ .has_ipfs = true,
+ .otg_reset_sspi = false,
+ .mbox = {
+ .cmd = 0xe4,
+ .data_in = 0xe8,
+ .data_out = 0xec,
+ .owner = 0xf0,
+ },
};
MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
@@ -1334,10 +1935,6 @@
"dvddio-pex",
"hvddio-pex",
"avdd-usb",
- "avdd-pll-utmip",
- "avdd-pll-uerefe",
- "dvdd-pex-pll",
- "hvdd-pex-pll-e",
};
static const struct tegra_xusb_phy_type tegra210_phy_types[] = {
@@ -1352,18 +1949,102 @@
.num_supplies = ARRAY_SIZE(tegra210_supply_names),
.phy_types = tegra210_phy_types,
.num_types = ARRAY_SIZE(tegra210_phy_types),
+ .context = &tegra124_xusb_context,
.ports = {
.usb2 = { .offset = 4, .count = 4, },
.hsic = { .offset = 8, .count = 1, },
.usb3 = { .offset = 0, .count = 4, },
},
.scale_ss_clock = false,
+ .has_ipfs = true,
+ .otg_reset_sspi = true,
+ .mbox = {
+ .cmd = 0xe4,
+ .data_in = 0xe8,
+ .data_out = 0xec,
+ .owner = 0xf0,
+ },
};
MODULE_FIRMWARE("nvidia/tegra210/xusb.bin");
+
+static const char * const tegra186_supply_names[] = {
+};
+MODULE_FIRMWARE("nvidia/tegra186/xusb.bin");
+
+static const struct tegra_xusb_phy_type tegra186_phy_types[] = {
+ { .name = "usb3", .num = 3, },
+ { .name = "usb2", .num = 3, },
+ { .name = "hsic", .num = 1, },
+};
+
+static const struct tegra_xusb_context_soc tegra186_xusb_context = {
+ .fpci = {
+ .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci),
+ .offsets = tegra124_xusb_context_fpci,
+ },
+};
+
+static const struct tegra_xusb_soc tegra186_soc = {
+ .firmware = "nvidia/tegra186/xusb.bin",
+ .supply_names = tegra186_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra186_supply_names),
+ .phy_types = tegra186_phy_types,
+ .num_types = ARRAY_SIZE(tegra186_phy_types),
+ .context = &tegra186_xusb_context,
+ .ports = {
+ .usb3 = { .offset = 0, .count = 3, },
+ .usb2 = { .offset = 3, .count = 3, },
+ .hsic = { .offset = 6, .count = 1, },
+ },
+ .scale_ss_clock = false,
+ .has_ipfs = false,
+ .otg_reset_sspi = false,
+ .mbox = {
+ .cmd = 0xe4,
+ .data_in = 0xe8,
+ .data_out = 0xec,
+ .owner = 0xf0,
+ },
+ .lpm_support = true,
+};
+
+static const char * const tegra194_supply_names[] = {
+};
+
+static const struct tegra_xusb_phy_type tegra194_phy_types[] = {
+ { .name = "usb3", .num = 4, },
+ { .name = "usb2", .num = 4, },
+};
+
+static const struct tegra_xusb_soc tegra194_soc = {
+ .firmware = "nvidia/tegra194/xusb.bin",
+ .supply_names = tegra194_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra194_supply_names),
+ .phy_types = tegra194_phy_types,
+ .num_types = ARRAY_SIZE(tegra194_phy_types),
+ .context = &tegra186_xusb_context,
+ .ports = {
+ .usb3 = { .offset = 0, .count = 4, },
+ .usb2 = { .offset = 4, .count = 4, },
+ },
+ .scale_ss_clock = false,
+ .has_ipfs = false,
+ .otg_reset_sspi = false,
+ .mbox = {
+ .cmd = 0x68,
+ .data_in = 0x6c,
+ .data_out = 0x70,
+ .owner = 0x74,
+ },
+ .lpm_support = true,
+};
+MODULE_FIRMWARE("nvidia/tegra194/xusb.bin");
static const struct of_device_id tegra_xusb_of_match[] = {
{ .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc },
{ .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc },
+ { .compatible = "nvidia,tegra186-xusb", .data = &tegra186_soc },
+ { .compatible = "nvidia,tegra194-xusb", .data = &tegra194_soc },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
@@ -1380,7 +2061,11 @@
static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+
xhci->quirks |= XHCI_PLAT;
+ if (tegra && tegra->soc->lpm_support)
+ xhci->quirks |= XHCI_LPM_SUPPORT;
}
static int tegra_xhci_setup(struct usb_hcd *hcd)
--
Gitblit v1.6.2