blob: 169ffd9714315f1b41d72bc4c18fb64b2acab351 [file] [log] [blame]
/*
* xhci-tegra.c - Nvidia xHCI host controller driver
*
* Copyright (c) 2013, 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,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/ioport.h>
#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_data/tegra_usb.h>
#include <linux/uaccess.h>
#include <linux/circ_buf.h>
#include <linux/vmalloc.h>
#include <linux/debugfs.h>
#include <linux/kthread.h>
#include <linux/gpio.h>
#include <linux/usb/otg.h>
#include <linux/clk/tegra.h>
#include <linux/tegra-powergate.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"
/* macros */
#define FW_IOCTL_LOG_DEQUEUE_LOW (4)
#define FW_IOCTL_LOG_DEQUEUE_HIGH (5)
#define FW_IOCTL_DATA_SHIFT (0)
#define FW_IOCTL_DATA_MASK (0x00ffffff)
#define FW_IOCTL_TYPE_SHIFT (24)
#define FW_IOCTL_TYPE_MASK (0xff000000)
#define FW_LOG_SIZE (sizeof(struct log_entry))
#define FW_LOG_COUNT (4096)
#define FW_LOG_RING_SIZE (FW_LOG_SIZE * FW_LOG_COUNT)
#define FW_LOG_PAYLOAD_SIZE (27)
#define DRIVER (0x01)
#define CIRC_BUF_SIZE (4 * (1 << 20)) /* 4MB */
#define FW_LOG_THREAD_RELAX (msecs_to_jiffies(100))
/* tegra_xhci_firmware_log.flags bits */
#define FW_LOG_CONTEXT_VALID (0)
#define FW_LOG_FILE_OPENED (1)
#define PAGE_SELECT_MASK 0xFFFFFE00
#define PAGE_SELECT_SHIFT 9
#define PAGE_OFFSET_MASK 0x000001FF
#define CSB_PAGE_SELECT(_addr) \
({ \
typecheck(u32, _addr); \
((_addr & PAGE_SELECT_MASK) >> PAGE_SELECT_SHIFT); \
})
#define CSB_PAGE_OFFSET(_addr) \
({ \
typecheck(u32, _addr); \
(_addr & PAGE_OFFSET_MASK); \
})
#define reg_dump(_dev, _base, _reg) \
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_USB_DEBOUNCE_DEL_0 0xec
#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16)
#define UTMIP_LINE_DEB_CNT_MASK (0xf << 16)
#define PMC_UTMIP_UHSIC_SLEEP_CFG_0 0x1fc
/* private data types */
/* command requests from the firmware */
enum MBOX_CMD_TYPE {
MBOX_CMD_MSG_ENABLED = 1,
MBOX_CMD_INC_FALC_CLOCK,
MBOX_CMD_DEC_FALC_CLOCK,
MBOX_CMD_INC_SSPI_CLOCK,
MBOX_CMD_DEC_SSPI_CLOCK, /* 5 */
MBOX_CMD_SET_BW,
MBOX_CMD_SET_SS_PWR_GATING,
MBOX_CMD_SET_SS_PWR_UNGATING, /* 8 */
MBOX_CMD_SAVE_DFE_CTLE_CTX,
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,
/* resp msg to ack above commands */
MBOX_CMD_ACK = 128,
MBOX_CMD_NACK
};
struct log_entry {
u32 sequence_no;
u8 data[FW_LOG_PAYLOAD_SIZE];
u8 owner;
};
/* Usb3 Firmware Cfg Table */
struct cfgtbl {
u32 boot_loadaddr_in_imem;
u32 boot_codedfi_offset;
u32 boot_codetag;
u32 boot_codesize;
/* Physical memory reserved by Bootloader/BIOS */
u32 phys_memaddr;
u16 reqphys_memsize;
u16 alloc_phys_memsize;
/* .rodata section */
u32 rodata_img_offset;
u32 rodata_section_start;
u32 rodata_section_end;
u32 main_fnaddr;
u32 fwimg_cksum;
u32 fwimg_created_time;
/* Fields that get filled by linker during linking phase
* or initialized in the FW code.
*/
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;
u8 init_ddirect;
u8 reserved[3];
u32 phys_addr_log_buffer;
u32 total_log_entries;
u32 dequeue_ptr;
/* Below two dummy variables are used to replace
* L2IMemSymTabOffsetInDFI and L2IMemSymTabSize in order to
* retain the size of struct _CFG_TBL used by other AP/Module.
*/
u32 dummy_var1;
u32 dummy_var2;
/* fwimg_len */
u32 fwimg_len;
u8 magic[8];
u32 SS_low_power_entry_timeout;
u8 num_hsic_port;
u8 padding[139]; /* padding bytes to makeup 256-bytes cfgtbl */
};
struct xusb_save_regs {
u32 msi_bar_sz;
u32 msi_axi_barst;
u32 msi_fpci_barst;
u32 msi_vec0;
u32 msi_en_vec0;
u32 fpci_error_masks;
u32 intr_mask;
u32 ipfs_intr_enable;
u32 ufpci_config;
u32 clkgate_hysteresis;
u32 xusb_host_mccif_fifo_cntrl;
/* PG does not mention below */
u32 hs_pls;
u32 fs_pls;
u32 hs_fs_speed;
u32 hs_fs_pp;
u32 cfg_aru;
u32 cfg_order;
u32 cfg_fladj;
u32 cfg_sid;
/* DFE and CTLE */
u32 tap1_val[XUSB_SS_PORT_COUNT];
u32 amp_val[XUSB_SS_PORT_COUNT];
u32 ctle_z_val[XUSB_SS_PORT_COUNT];
u32 ctle_g_val[XUSB_SS_PORT_COUNT];
};
struct tegra_xhci_firmware {
void *data; /* kernel virtual address */
size_t size; /* firmware size */
dma_addr_t dma; /* dma address for controller */
};
struct tegra_xhci_firmware_log {
dma_addr_t phys_addr; /* dma-able address */
void *virt_addr; /* kernel va of the shared log buffer */
struct log_entry *dequeue; /* current dequeue pointer (va) */
struct circ_buf circ; /* big circular buffer */
u32 seq; /* log sequence number */
struct task_struct *thread; /* a thread to consume log */
struct mutex mutex;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
wait_queue_head_t intr_wait;
struct dentry *path;
struct dentry *log_file;
unsigned long flags;
};
/* structure to hold the offsets of padctl registers */
struct tegra_xusb_padctl_regs {
u16 boot_media_0;
u16 usb2_pad_mux_0;
u16 usb2_port_cap_0;
u16 snps_oc_map_0;
u16 usb2_oc_map_0;
u16 ss_port_map_0;
u16 oc_det_0;
u16 elpg_program_0;
u16 usb2_bchrg_otgpad0_ctl0_0;
u16 usb2_bchrg_otgpad0_ctl1_0;
u16 usb2_bchrg_otgpad1_ctl0_0;
u16 usb2_bchrg_otgpad1_ctl1_0;
u16 usb2_bchrg_otgpad2_ctl0_0;
u16 usb2_bchrg_otgpad2_ctl1_0;
u16 usb2_bchrg_bias_pad_0;
u16 usb2_bchrg_tdcd_dbnc_timer_0;
u16 iophy_pll_p0_ctl1_0;
u16 iophy_pll_p0_ctl2_0;
u16 iophy_pll_p0_ctl3_0;
u16 iophy_pll_p0_ctl4_0;
u16 iophy_usb3_pad0_ctl1_0;
u16 iophy_usb3_pad1_ctl1_0;
u16 iophy_usb3_pad0_ctl2_0;
u16 iophy_usb3_pad1_ctl2_0;
u16 iophy_usb3_pad0_ctl3_0;
u16 iophy_usb3_pad1_ctl3_0;
u16 iophy_usb3_pad0_ctl4_0;
u16 iophy_usb3_pad1_ctl4_0;
u16 iophy_misc_pad_p0_ctl1_0;
u16 iophy_misc_pad_p1_ctl1_0;
u16 iophy_misc_pad_p0_ctl2_0;
u16 iophy_misc_pad_p1_ctl2_0;
u16 iophy_misc_pad_p0_ctl3_0;
u16 iophy_misc_pad_p1_ctl3_0;
u16 iophy_misc_pad_p0_ctl4_0;
u16 iophy_misc_pad_p1_ctl4_0;
u16 iophy_misc_pad_p0_ctl5_0;
u16 iophy_misc_pad_p1_ctl5_0;
u16 iophy_misc_pad_p0_ctl6_0;
u16 iophy_misc_pad_p1_ctl6_0;
u16 usb2_otg_pad0_ctl0_0;
u16 usb2_otg_pad1_ctl0_0;
u16 usb2_otg_pad2_ctl0_0;
u16 usb2_otg_pad0_ctl1_0;
u16 usb2_otg_pad1_ctl1_0;
u16 usb2_otg_pad2_ctl1_0;
u16 usb2_bias_pad_ctl0_0;
u16 usb2_bias_pad_ctl1_0;
u16 usb2_hsic_pad0_ctl0_0;
u16 usb2_hsic_pad1_ctl0_0;
u16 usb2_hsic_pad0_ctl1_0;
u16 usb2_hsic_pad1_ctl1_0;
u16 usb2_hsic_pad0_ctl2_0;
u16 usb2_hsic_pad1_ctl2_0;
u16 ulpi_link_trim_ctl0;
u16 ulpi_null_clk_trim_ctl0;
u16 hsic_strb_trim_ctl0;
u16 wake_ctl0;
u16 pm_spare0;
u16 iophy_misc_pad_p2_ctl1_0;
u16 iophy_misc_pad_p3_ctl1_0;
u16 iophy_misc_pad_p4_ctl1_0;
u16 iophy_misc_pad_p2_ctl2_0;
u16 iophy_misc_pad_p3_ctl2_0;
u16 iophy_misc_pad_p4_ctl2_0;
u16 iophy_misc_pad_p2_ctl3_0;
u16 iophy_misc_pad_p3_ctl3_0;
u16 iophy_misc_pad_p4_ctl3_0;
u16 iophy_misc_pad_p2_ctl4_0;
u16 iophy_misc_pad_p3_ctl4_0;
u16 iophy_misc_pad_p4_ctl4_0;
u16 iophy_misc_pad_p2_ctl5_0;
u16 iophy_misc_pad_p3_ctl5_0;
u16 iophy_misc_pad_p4_ctl5_0;
u16 iophy_misc_pad_p2_ctl6_0;
u16 iophy_misc_pad_p3_ctl6_0;
u16 iophy_misc_pad_p4_ctl6_0;
u16 usb3_pad_mux_0;
u16 iophy_pll_s0_ctl1_0;
u16 iophy_pll_s0_ctl2_0;
u16 iophy_pll_s0_ctl3_0;
u16 iophy_pll_s0_ctl4_0;
u16 iophy_misc_pad_s0_ctl1_0;
u16 iophy_misc_pad_s0_ctl2_0;
u16 iophy_misc_pad_s0_ctl3_0;
u16 iophy_misc_pad_s0_ctl4_0;
u16 iophy_misc_pad_s0_ctl5_0;
u16 iophy_misc_pad_s0_ctl6_0;
};
struct tegra_xhci_hcd {
struct platform_device *pdev;
struct xhci_hcd *xhci;
u16 device_id;
spinlock_t lock;
struct mutex sync_lock;
int smi_irq;
int padctl_irq;
int usb3_irq;
int usb2_irq;
bool ss_wake_event;
bool ss_pwr_gated;
bool host_pwr_gated;
bool hs_wake_event;
bool host_resume_req;
bool lp0_exit;
bool dfe_ctx_saved[XUSB_SS_PORT_COUNT];
bool ctle_ctx_saved[XUSB_SS_PORT_COUNT];
unsigned long last_jiffies;
unsigned long host_phy_base;
void __iomem *host_phy_virt_base;
void __iomem *padctl_base;
void __iomem *fpci_base;
void __iomem *ipfs_base;
struct tegra_xusb_platform_data *pdata;
struct tegra_xusb_board_data *bdata;
struct tegra_xusb_padctl_regs *padregs;
/* mailbox variables */
struct mutex mbox_lock;
u32 mbox_owner;
u32 cmd_type;
u32 cmd_data;
struct regulator *xusb_s5p0v_reg;
struct regulator *xusb_s5p0v1_reg;
struct regulator *xusb_s5p0v2_reg;
struct regulator *xusb_s1p05v_reg;
struct regulator *xusb_s3p3v_reg;
struct regulator *xusb_s1p8v_reg;
struct regulator *vddio_hsic_reg;
int vddio_hsic_refcnt;
struct work_struct mbox_work;
struct work_struct ss_elpg_exit_work;
struct work_struct host_elpg_exit_work;
struct clk *host_clk;
struct clk *ss_clk;
/* XUSB Falcon SuperSpeed Clock */
struct clk *falc_clk;
/* EMC Clock */
struct clk *emc_clk;
/* XUSB SS PI Clock */
struct clk *ss_src_clk;
/* PLLE Clock */
struct clk *plle_clk;
struct clk *pll_u_480M;
struct clk *clk_m;
/* refPLLE clk */
struct clk *pll_re_vco_clk;
/*
* XUSB/IPFS specific registers these need to be saved/restored in
* addition to spec defined registers
*/
struct xusb_save_regs sregs;
bool usb2_rh_suspend;
bool usb3_rh_suspend;
bool hc_in_elpg;
/* otg transceiver */
struct usb_phy *transceiver;
struct notifier_block otgnb;
unsigned long usb2_rh_remote_wakeup_ports; /* one bit per port */
unsigned long usb3_rh_remote_wakeup_ports; /* one bit per port */
/* firmware loading related */
struct tegra_xhci_firmware firmware;
struct tegra_xhci_firmware_log log;
};
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));
/* 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)
{
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)
{
int err = 0;
if (!tegra->bdata->gpio_controls_muxed_ss_lanes)
return;
if (tegra->bdata->lane_owner & BIT(0)) {
/* USB3_SS port1 is using SATA lane so set (MUX_SATA)
* GPIO P11 to '0'
*/
err = gpio_request((tegra->bdata->gpio_ss1_sata & 0xffff),
"gpio_ss1_sata");
if (err < 0)
pr_err("%s: gpio_ss1_sata gpio_request failed %d\n",
__func__, err);
err = gpio_direction_output((tegra->bdata->gpio_ss1_sata
& 0xffff), 1);
if (err < 0)
pr_err("%s: gpio_ss1_sata gpio_direction failed %d\n",
__func__, err);
__gpio_set_value((tegra->bdata->gpio_ss1_sata & 0xffff),
((tegra->bdata->gpio_ss1_sata >> 16)));
}
}
static u32 xhci_read_portsc(struct xhci_hcd *xhci, unsigned int port)
{
int num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
__le32 __iomem *addr;
if (port >= num_ports) {
xhci_err(xhci, "%s invalid port %u\n", __func__, port);
return -1;
}
addr = &xhci->op_regs->port_status_base + (NUM_PORT_REGS * port);
return xhci_readl(xhci, addr);
}
static void debug_print_portsc(struct xhci_hcd *xhci)
{
__le32 __iomem *addr = &xhci->op_regs->port_status_base;
u32 reg;
int i;
int ports;
ports = HCS_MAX_PORTS(xhci->hcs_params1);
for (i = 0; i < ports; i++) {
reg = xhci_read_portsc(xhci, i);
xhci_dbg(xhci, "@%p port %d status reg = 0x%x\n",
addr, i, (unsigned int) reg);
addr += NUM_PORT_REGS;
}
}
static void tegra_xhci_war_for_tctrl_rctrl(struct tegra_xhci_hcd *tegra);
static bool is_otg_host(struct tegra_xhci_hcd *tegra)
{
if (!tegra->transceiver)
return true;
else if (tegra->transceiver->state == OTG_STATE_A_HOST)
return true;
else
return false;
}
static int update_speed(struct tegra_xhci_hcd *tegra, u8 port)
{
struct usb_hcd *hcd = xhci_to_hcd(tegra->xhci);
u32 portsc;
portsc = readl(hcd->regs + BAR0_XHCI_OP_PORTSC(port));
if (DEV_FULLSPEED(portsc))
return USB_PMC_PORT_SPEED_FULL;
else if (DEV_HIGHSPEED(portsc))
return USB_PMC_PORT_SPEED_HIGH;
else if (DEV_LOWSPEED(portsc))
return USB_PMC_PORT_SPEED_LOW;
else if (DEV_SUPERSPEED(portsc))
return USB_PMC_PORT_SPEED_SUPER;
else
return USB_PMC_PORT_SPEED_UNKNOWN;
}
static void pmc_init(struct tegra_xhci_hcd *tegra)
{
struct tegra_usb_pmc_data *pmc;
struct device *dev = &tegra->pdev->dev;
int pad;
for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
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;
pmc->phy_type = TEGRA_USB_PHY_INTF_UTMI;
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)
{
struct tegra_usb_pmc_data *pmc;
struct device *dev = &tegra->pdev->dev;
u32 portsc;
int port;
int 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);
if (((int) portsc != -1) && (portsc & PORT_CONNECT))
pmc->pmc_ops->setup_pmc_wake_detect(pmc);
}
for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
if (BIT(XUSB_UTMI_INDEX + pad) & tegra->bdata->portmap) {
dev_dbg(dev, "%s utmi pad %d\n", __func__, pad);
pmc = &pmc_data[pad];
pmc->port_speed = update_speed(tegra, pad);
if (pad == 0) {
if (is_otg_host(tegra))
pmc->pmc_ops->setup_pmc_wake_detect(
pmc);
} else
pmc->pmc_ops->setup_pmc_wake_detect(pmc);
}
}
}
static void pmc_disable_bus_ctrl(struct tegra_xhci_hcd *tegra)
{
struct tegra_usb_pmc_data *pmc;
struct device *dev = &tegra->pdev->dev;
int 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);
}
for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
if (BIT(XUSB_UTMI_INDEX + pad) & tegra->bdata->portmap) {
dev_dbg(dev, "%s utmi pad %d\n", __func__, pad);
pmc = &pmc_data[pad];
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
}
}
}
u32 csb_read(struct tegra_xhci_hcd *tegra, u32 addr)
{
void __iomem *fpci_base = tegra->fpci_base;
struct platform_device *pdev = tegra->pdev;
u32 input_addr;
u32 data;
u32 csb_page_select;
/* to select the appropriate CSB page to write to */
csb_page_select = CSB_PAGE_SELECT(addr);
dev_dbg(&pdev->dev, "csb_read: csb_page_select= 0x%08x\n",
csb_page_select);
iowrite32(csb_page_select, fpci_base + XUSB_CFG_ARU_C11_CSBRANGE);
/* selects the appropriate offset in the page to read from */
input_addr = CSB_PAGE_OFFSET(addr);
data = ioread32(fpci_base + XUSB_CFG_CSB_BASE_ADDR + input_addr);
dev_dbg(&pdev->dev, "csb_read: input_addr = 0x%08x data = 0x%08x\n",
input_addr, data);
return data;
}
void csb_write(struct tegra_xhci_hcd *tegra, u32 addr, u32 data)
{
void __iomem *fpci_base = tegra->fpci_base;
struct platform_device *pdev = tegra->pdev;
u32 input_addr;
u32 csb_page_select;
/* to select the appropriate CSB page to write to */
csb_page_select = CSB_PAGE_SELECT(addr);
dev_dbg(&pdev->dev, "csb_write:csb_page_selectx = 0x%08x\n",
csb_page_select);
iowrite32(csb_page_select, fpci_base + XUSB_CFG_ARU_C11_CSBRANGE);
/* selects the appropriate offset in the page to write to */
input_addr = CSB_PAGE_OFFSET(addr);
iowrite32(data, fpci_base + XUSB_CFG_CSB_BASE_ADDR + input_addr);
dev_dbg(&pdev->dev, "csb_write: input_addr = 0x%08x data = %0x08x\n",
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
* is the last one, it returns with the first one.
*
* @param log The tegra_xhci_firmware_log context.
* @param this The current log entry.
* @return The log entry which is next to the current one.
*/
static inline struct log_entry *fw_log_next(
struct tegra_xhci_firmware_log *log, struct log_entry *this)
{
struct log_entry *first = (struct log_entry *) log->virt_addr;
struct log_entry *last = first + FW_LOG_COUNT - 1;
WARN((this < first) || (this > last), "%s: invalid input\n", __func__);
return (this == last) ? first : (this + 1);
}
/**
* fw_log_update_dequeue_pointer - update dequeue pointer to both firmware and
* tegra_xhci_firmware_log.dequeue.
*
* @param log The tegra_xhci_firmware_log context.
* @param n Counts of log entries to fast-forward.
*/
static inline void fw_log_update_deq_pointer(
struct tegra_xhci_firmware_log *log, int n)
{
struct tegra_xhci_hcd *tegra =
container_of(log, struct tegra_xhci_hcd, log);
struct device *dev = &tegra->pdev->dev;
struct log_entry *deq = tegra->log.dequeue;
dma_addr_t physical_addr;
u32 reg;
dev_dbg(dev, "curr 0x%p fast-forward %d entries\n", deq, n);
while (n-- > 0)
deq = fw_log_next(log, deq);
tegra->log.dequeue = deq;
physical_addr = tegra->log.phys_addr +
((u8 *)deq - (u8 *)tegra->log.virt_addr);
/* update dequeue pointer to firmware */
reg = (FW_IOCTL_LOG_DEQUEUE_LOW << FW_IOCTL_TYPE_SHIFT);
reg |= (physical_addr & 0xffff); /* lower 16-bits */
iowrite32(reg, tegra->fpci_base + XUSB_CFG_ARU_FW_SCRATCH);
reg = (FW_IOCTL_LOG_DEQUEUE_HIGH << FW_IOCTL_TYPE_SHIFT);
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);
}
static inline bool circ_buffer_full(struct circ_buf *circ)
{
int space = CIRC_SPACE(circ->head, circ->tail, CIRC_BUF_SIZE);
return (space <= FW_LOG_SIZE);
}
static inline bool fw_log_available(struct tegra_xhci_hcd *tegra)
{
return (tegra->log.dequeue->owner == DRIVER);
}
/**
* fw_log_wait_empty_timeout - wait firmware log thread to clean up shared
* log buffer.
* @param tegra: tegra_xhci_hcd context
* @param msec: timeout value in millisecond
* @return true: shared log buffer is empty,
* false: shared log buffer isn't empty.
*/
static inline bool fw_log_wait_empty_timeout(struct tegra_xhci_hcd *tegra,
unsigned timeout)
{
unsigned long target = jiffies + msecs_to_jiffies(timeout);
bool ret;
mutex_lock(&tegra->log.mutex);
while (fw_log_available(tegra) && time_is_after_jiffies(target)) {
mutex_unlock(&tegra->log.mutex);
usleep_range(1000, 2000);
mutex_lock(&tegra->log.mutex);
}
ret = fw_log_available(tegra);
mutex_unlock(&tegra->log.mutex);
return ret;
}
/**
* fw_log_copy - copy firmware log from device's buffer to driver's circular
* buffer.
* @param tegra tegra_xhci_hcd context
* @return true, We still have firmware log in device's buffer to copy.
* This function returned due the driver's circular buffer
* is full. Caller should invoke this function again as
* soon as there is space in driver's circular buffer.
* false, Device's buffer is empty.
*/
static inline bool fw_log_copy(struct tegra_xhci_hcd *tegra)
{
struct device *dev = &tegra->pdev->dev;
struct circ_buf *circ = &tegra->log.circ;
int head, tail;
int buffer_len, copy_len;
struct log_entry *entry;
struct log_entry *first = tegra->log.virt_addr;
while (fw_log_available(tegra)) {
/* calculate maximum contiguous driver buffer length */
head = circ->head;
tail = ACCESS_ONCE(circ->tail);
buffer_len = CIRC_SPACE_TO_END(head, tail, CIRC_BUF_SIZE);
/* round down to FW_LOG_SIZE */
buffer_len -= (buffer_len % FW_LOG_SIZE);
if (!buffer_len)
return true; /* log available but no space left */
/* calculate maximum contiguous log copy length */
entry = tegra->log.dequeue;
copy_len = 0;
do {
if (tegra->log.seq != entry->sequence_no) {
dev_warn(dev,
"%s: discontinuous seq no, expect %u get %u\n",
__func__, tegra->log.seq, entry->sequence_no);
}
tegra->log.seq = entry->sequence_no + 1;
copy_len += FW_LOG_SIZE;
buffer_len -= FW_LOG_SIZE;
if (!buffer_len)
break; /* no space left */
entry = fw_log_next(&tegra->log, entry);
} while ((entry->owner == DRIVER) && (entry != first));
memcpy(&circ->buf[head], tegra->log.dequeue, copy_len);
memset(tegra->log.dequeue, 0, copy_len);
circ->head = (circ->head + copy_len) & (CIRC_BUF_SIZE - 1);
mb();
fw_log_update_deq_pointer(&tegra->log, copy_len/FW_LOG_SIZE);
dev_dbg(dev, "copied %d entries, new dequeue 0x%p\n",
copy_len/FW_LOG_SIZE, tegra->log.dequeue);
wake_up_interruptible(&tegra->log.read_wait);
}
return false;
}
static int fw_log_thread(void *data)
{
struct tegra_xhci_hcd *tegra = data;
struct device *dev = &tegra->pdev->dev;
struct circ_buf *circ = &tegra->log.circ;
bool logs_left;
dev_dbg(dev, "start firmware log thread\n");
do {
mutex_lock(&tegra->log.mutex);
if (circ_buffer_full(circ)) {
mutex_unlock(&tegra->log.mutex);
dev_info(dev, "%s: circ buffer full\n", __func__);
wait_event_interruptible(tegra->log.write_wait,
kthread_should_stop() || !circ_buffer_full(circ));
mutex_lock(&tegra->log.mutex);
}
logs_left = fw_log_copy(tegra);
mutex_unlock(&tegra->log.mutex);
/* relax if no logs left */
if (!logs_left)
wait_event_interruptible_timeout(tegra->log.intr_wait,
fw_log_available(tegra), FW_LOG_THREAD_RELAX);
} while (!kthread_should_stop());
dev_dbg(dev, "stop firmware log thread\n");
return 0;
}
static inline bool circ_buffer_empty(struct circ_buf *circ)
{
return (CIRC_CNT(circ->head, circ->tail, CIRC_BUF_SIZE) == 0);
}
static ssize_t fw_log_file_read(struct file *file, char __user *buf,
size_t count, loff_t *offp)
{
struct tegra_xhci_hcd *tegra = file->private_data;
struct platform_device *pdev = tegra->pdev;
struct circ_buf *circ = &tegra->log.circ;
int head, tail;
size_t n = 0;
int s;
mutex_lock(&tegra->log.mutex);
while (circ_buffer_empty(circ)) {
mutex_unlock(&tegra->log.mutex);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN; /* non-blocking read */
dev_dbg(&pdev->dev, "%s: nothing to read\n", __func__);
if (wait_event_interruptible(tegra->log.read_wait,
!circ_buffer_empty(circ)))
return -ERESTARTSYS;
if (mutex_lock_interruptible(&tegra->log.mutex))
return -ERESTARTSYS;
}
while (count > 0) {
head = ACCESS_ONCE(circ->head);
tail = circ->tail;
s = min_t(int, count,
CIRC_CNT_TO_END(head, tail, CIRC_BUF_SIZE));
if (s > 0) {
if (copy_to_user(&buf[n], &circ->buf[tail], s)) {
dev_warn(&pdev->dev, "copy_to_user failed\n");
mutex_unlock(&tegra->log.mutex);
return -EFAULT;
}
circ->tail = (circ->tail + s) & (CIRC_BUF_SIZE - 1);
count -= s;
n += s;
} else
break;
}
mutex_unlock(&tegra->log.mutex);
wake_up_interruptible(&tegra->log.write_wait);
dev_dbg(&pdev->dev, "%s: %d bytes\n", __func__, n);
return n;
}
static int fw_log_file_open(struct inode *inode, struct file *file)
{
struct tegra_xhci_hcd *tegra;
file->private_data = inode->i_private;
tegra = file->private_data;
if (test_and_set_bit(FW_LOG_FILE_OPENED, &tegra->log.flags)) {
dev_info(&tegra->pdev->dev, "%s: already opened\n", __func__);
return -EBUSY;
}
return 0;
}
static int fw_log_file_close(struct inode *inode, struct file *file)
{
struct tegra_xhci_hcd *tegra = file->private_data;
clear_bit(FW_LOG_FILE_OPENED, &tegra->log.flags);
return 0;
}
static const struct file_operations firmware_log_fops = {
.open = fw_log_file_open,
.release = fw_log_file_close,
.read = fw_log_file_read,
.owner = THIS_MODULE,
};
static int fw_log_init(struct tegra_xhci_hcd *tegra)
{
struct platform_device *pdev = tegra->pdev;
int rc = 0;
/* allocate buffer to be shared between driver and firmware */
tegra->log.virt_addr = dma_alloc_writecombine(&pdev->dev,
FW_LOG_RING_SIZE, &tegra->log.phys_addr, GFP_KERNEL);
if (!tegra->log.virt_addr) {
dev_err(&pdev->dev, "dma_alloc_writecombine() size %d failed\n",
FW_LOG_RING_SIZE);
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);
memset(tegra->log.virt_addr, 0, FW_LOG_RING_SIZE);
tegra->log.dequeue = tegra->log.virt_addr;
tegra->log.circ.buf = vmalloc(CIRC_BUF_SIZE);
if (!tegra->log.circ.buf) {
dev_err(&pdev->dev, "vmalloc size %d failed\n", CIRC_BUF_SIZE);
rc = -ENOMEM;
goto error_free_dma;
}
tegra->log.circ.head = 0;
tegra->log.circ.tail = 0;
init_waitqueue_head(&tegra->log.read_wait);
init_waitqueue_head(&tegra->log.write_wait);
init_waitqueue_head(&tegra->log.intr_wait);
mutex_init(&tegra->log.mutex);
tegra->log.path = debugfs_create_dir("tegra_xhci", NULL);
if (IS_ERR_OR_NULL(tegra->log.path)) {
dev_warn(&pdev->dev, "debugfs_create_dir() failed\n");
rc = -ENOMEM;
goto error_free_mem;
}
tegra->log.log_file = debugfs_create_file("firmware_log",
S_IRUGO, tegra->log.path, tegra, &firmware_log_fops);
if ((!tegra->log.log_file) ||
(tegra->log.log_file == ERR_PTR(-ENODEV))) {
dev_warn(&pdev->dev, "debugfs_create_file() failed\n");
rc = -ENOMEM;
goto error_remove_debugfs_path;
}
tegra->log.thread = kthread_run(fw_log_thread, tegra, "xusb-fw-log");
if (IS_ERR(tegra->log.thread)) {
dev_warn(&pdev->dev, "kthread_run() failed\n");
rc = -ENOMEM;
goto error_remove_debugfs_file;
}
set_bit(FW_LOG_CONTEXT_VALID, &tegra->log.flags);
return rc;
error_remove_debugfs_file:
debugfs_remove(tegra->log.log_file);
error_remove_debugfs_path:
debugfs_remove(tegra->log.path);
error_free_mem:
vfree(tegra->log.circ.buf);
error_free_dma:
dma_free_writecombine(&pdev->dev, FW_LOG_RING_SIZE,
tegra->log.virt_addr, tegra->log.phys_addr);
memset(&tegra->log, sizeof(tegra->log), 0);
return rc;
}
static void fw_log_deinit(struct tegra_xhci_hcd *tegra)
{
struct platform_device *pdev = tegra->pdev;
if (test_and_clear_bit(FW_LOG_CONTEXT_VALID, &tegra->log.flags)) {
debugfs_remove(tegra->log.log_file);
debugfs_remove(tegra->log.path);
wake_up_interruptible(&tegra->log.read_wait);
wake_up_interruptible(&tegra->log.write_wait);
kthread_stop(tegra->log.thread);
mutex_lock(&tegra->log.mutex);
dma_free_writecombine(&pdev->dev, FW_LOG_RING_SIZE,
tegra->log.virt_addr, tegra->log.phys_addr);
vfree(tegra->log.circ.buf);
tegra->log.circ.head = tegra->log.circ.tail = 0;
mutex_unlock(&tegra->log.mutex);
mutex_destroy(&tegra->log.mutex);
}
}
/* hsic pad operations */
/*
* HSIC pads need VDDIO_HSIC power rail turned on to be functional. There is
* only one VDDIO_HSIC power rail shared by all HSIC pads.
*/
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;
int ret;
if (tegra->vddio_hsic_reg)
goto done;
tegra->vddio_hsic_reg = devm_regulator_get(dev, supply->vddio_hsic);
if (IS_ERR_OR_NULL(tegra->vddio_hsic_reg)) {
dev_err(dev, "%s get vddio_hsic failed\n", __func__);
ret = PTR_ERR(tegra->vddio_hsic_reg);
goto get_failed;
}
dev_dbg(dev, "%s regulator_enable vddio_hsic\n", __func__);
ret = regulator_enable(tegra->vddio_hsic_reg);
if (ret < 0) {
dev_err(dev, "%s enable vddio_hsic failed\n", __func__);
goto enable_failed;
}
done:
tegra->vddio_hsic_refcnt++;
WARN(tegra->vddio_hsic_refcnt > XUSB_HSIC_COUNT,
"vddio_hsic_refcnt exceeds\n");
return 0;
enable_failed:
devm_regulator_put(tegra->vddio_hsic_reg);
get_failed:
tegra->vddio_hsic_reg = NULL;
return ret;
}
static int hsic_power_rail_disable(struct tegra_xhci_hcd *tegra)
{
struct device *dev = &tegra->pdev->dev;
int ret;
WARN_ON(!tegra->vddio_hsic_reg || !tegra->vddio_hsic_refcnt);
tegra->vddio_hsic_refcnt--;
if (tegra->vddio_hsic_refcnt)
return 0;
dev_dbg(dev, "%s regulator_disable vddio_hsic\n", __func__);
ret = regulator_disable(tegra->vddio_hsic_reg);
if (ret < 0) {
dev_err(dev, "%s disable vddio_hsic failed\n", __func__);
tegra->vddio_hsic_refcnt++;
return ret;
}
devm_regulator_put(tegra->vddio_hsic_reg);
tegra->vddio_hsic_reg = NULL;
return 0;
}
static int hsic_pad_enable(struct tegra_xhci_hcd *tegra, unsigned pad)
{
struct device *dev = &tegra->pdev->dev;
void __iomem *base = tegra->padctl_base;
struct tegra_xusb_hsic_config *hsic = &tegra->bdata->hsic[pad];
u32 reg;
if (pad >= XUSB_HSIC_COUNT) {
dev_err(dev, "%s invalid HSIC pad number %d\n", __func__, pad);
return -EINVAL;
}
dev_dbg(dev, "%s pad %u\n", __func__, pad);
reg = readl(base + HSIC_PAD_CTL_2(pad));
reg &= ~(RX_STROBE_TRIM(~0) | RX_DATA_TRIM(~0));
reg |= RX_STROBE_TRIM(hsic->rx_strobe_trim);
reg |= RX_DATA_TRIM(hsic->rx_data_trim);
writel(reg, base + HSIC_PAD_CTL_2(pad));
reg = readl(base + HSIC_PAD_CTL_0(pad));
reg &= ~(TX_RTUNEP(~0) | TX_RTUNEN(~0) | TX_SLEWP(~0) | TX_SLEWN(~0));
reg |= TX_RTUNEP(hsic->tx_rtune_p);
reg |= TX_RTUNEN(hsic->tx_rtune_n);
reg |= TX_SLEWP(hsic->tx_slew_p);
reg |= TX_SLEWN(hsic->tx_slew_n);
writel(reg, base + HSIC_PAD_CTL_0(pad));
reg = readl(base + HSIC_PAD_CTL_1(pad));
reg &= ~(RPD_DATA | RPD_STROBE | RPU_DATA | RPU_STROBE);
reg |= (RPD_DATA | RPU_STROBE); /* keep HSIC in IDLE */
if (hsic->auto_term_en)
reg |= AUTO_TERM_EN;
else
reg &= ~AUTO_TERM_EN;
reg &= ~(PD_RX | HSIC_PD_ZI | PD_TRX | PD_TX);
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);
writel(reg, base + HSIC_STRB_TRIM_CONTROL);
reg = readl(base + USB2_PAD_MUX);
reg |= USB2_HSIC_PAD_PORT(pad);
writel(reg, base + USB2_PAD_MUX);
reg_dump(dev, base, HSIC_PAD_CTL_0(pad));
reg_dump(dev, base, HSIC_PAD_CTL_1(pad));
reg_dump(dev, base, HSIC_PAD_CTL_2(pad));
reg_dump(dev, base, HSIC_STRB_TRIM_CONTROL);
reg_dump(dev, base, USB2_PAD_MUX);
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;
void __iomem *base = tegra->padctl_base;
u32 reg;
if (pad >= XUSB_HSIC_COUNT) {
dev_err(dev, "%s invalid HSIC pad number %d\n", __func__, pad);
return -EINVAL;
}
dev_dbg(dev, "%s pad %u\n", __func__, pad);
reg = readl(base + USB2_PAD_MUX);
reg &= ~USB2_HSIC_PAD_PORT(pad);
writel(reg, base + USB2_PAD_MUX);
reg = readl(base + HSIC_PAD_CTL_1(pad));
reg |= (PD_RX | HSIC_PD_ZI | PD_TRX | PD_TX);
writel(reg, base + HSIC_PAD_CTL_1(pad));
reg_dump(dev, base, HSIC_PAD_CTL_0(pad));
reg_dump(dev, base, HSIC_PAD_CTL_1(pad));
reg_dump(dev, base, HSIC_PAD_CTL_2(pad));
reg_dump(dev, base, USB2_PAD_MUX);
return 0;
}
enum hsic_pad_pupd {
PUPD_DISABLE = 0,
PUPD_IDLE,
PUPD_RESET
};
static int hsic_pad_pupd_set(struct tegra_xhci_hcd *tegra, unsigned pad,
enum hsic_pad_pupd pupd)
{
struct device *dev = &tegra->pdev->dev;
void __iomem *base = tegra->padctl_base;
u32 reg;
if (pad >= XUSB_HSIC_COUNT) {
dev_err(dev, "%s invalid HSIC pad number %u\n", __func__, pad);
return -EINVAL;
}
dev_dbg(dev, "%s pad %u pupd %d\n", __func__, pad, pupd);
reg = readl(base + HSIC_PAD_CTL_1(pad));
reg &= ~(RPD_DATA | RPD_STROBE | RPU_DATA | RPU_STROBE);
if (pupd == PUPD_IDLE)
reg |= (RPD_DATA | RPU_STROBE);
else if (pupd == PUPD_RESET)
reg |= (RPD_DATA | RPD_STROBE);
else if (pupd != PUPD_DISABLE) {
dev_err(dev, "%s invalid pupd %d\n", __func__, pupd);
return -EINVAL;
}
writel(reg, base + HSIC_PAD_CTL_1(pad));
reg_dump(dev, base, HSIC_PAD_CTL_1(pad));
return 0;
}
static void tegra_xhci_debug_read_pads(struct tegra_xhci_hcd *tegra)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
struct xhci_hcd *xhci = tegra->xhci;
u32 reg;
xhci_info(xhci, "============ PADCTL VALUES START =================\n");
reg = readl(tegra->padctl_base + padregs->usb2_pad_mux_0);
xhci_info(xhci, " PAD MUX = %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_port_cap_0);
xhci_info(xhci, " PORT CAP = %x\n", reg);
reg = readl(tegra->padctl_base + padregs->snps_oc_map_0);
xhci_info(xhci, " SNPS OC MAP = %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_oc_map_0);
xhci_info(xhci, " USB2 OC MAP = %x\n", reg);
reg = readl(tegra->padctl_base + padregs->ss_port_map_0);
xhci_info(xhci, " SS PORT MAP = %x\n", reg);
reg = readl(tegra->padctl_base + padregs->oc_det_0);
xhci_info(xhci, " OC DET 0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->iophy_usb3_pad0_ctl2_0);
xhci_info(xhci, " iophy_usb3_pad0_ctl2_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->iophy_usb3_pad1_ctl2_0);
xhci_info(xhci, " iophy_usb3_pad1_ctl2_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_otg_pad0_ctl0_0);
xhci_info(xhci, " usb2_otg_pad0_ctl0_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_otg_pad1_ctl0_0);
xhci_info(xhci, " usb2_otg_pad1_ctl0_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_otg_pad0_ctl1_0);
xhci_info(xhci, " usb2_otg_pad0_ctl1_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_otg_pad1_ctl1_0);
xhci_info(xhci, " usb2_otg_pad1_ctl1_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
xhci_info(xhci, " usb2_bias_pad_ctl0_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_hsic_pad0_ctl0_0);
xhci_info(xhci, " usb2_hsic_pad0_ctl0_0= %x\n", reg);
reg = readl(tegra->padctl_base + padregs->usb2_hsic_pad1_ctl0_0);
xhci_info(xhci, " usb2_hsic_pad1_ctl0_0= %x\n", reg);
xhci_info(xhci, "============ PADCTL VALUES END=================\n");
}
static void tegra_xhci_cfg(struct tegra_xhci_hcd *tegra)
{
u32 reg;
reg = readl(tegra->ipfs_base + IPFS_XUSB_HOST_CONFIGURATION_0);
reg |= IPFS_EN_FPCI;
writel(reg, tegra->ipfs_base + IPFS_XUSB_HOST_CONFIGURATION_0);
udelay(10);
/* Program Bar0 Space */
reg = readl(tegra->fpci_base + XUSB_CFG_4);
reg |= tegra->host_phy_base;
writel(reg, tegra->fpci_base + XUSB_CFG_4);
usleep_range(100, 200);
/* Enable Bus Master */
reg = readl(tegra->fpci_base + XUSB_CFG_1);
reg |= 0x7;
writel(reg, tegra->fpci_base + XUSB_CFG_1);
/* Set intr mask to enable intr assertion */
reg = readl(tegra->ipfs_base + IPFS_XUSB_HOST_INTR_MASK_0);
reg |= IPFS_IP_INT_MASK;
writel(reg, tegra->ipfs_base + IPFS_XUSB_HOST_INTR_MASK_0);
/* Set hysteris to 0x80 */
writel(0x80, tegra->ipfs_base + IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
}
static int tegra_xusb_regulator_init(struct tegra_xhci_hcd *tegra,
struct platform_device *pdev)
{
struct tegra_xusb_regulator_name *supply = &tegra->bdata->supply;
int err = 0;
tegra->xusb_s3p3v_reg =
devm_regulator_get(&pdev->dev, supply->s3p3v);
if (IS_ERR(tegra->xusb_s3p3v_reg)) {
dev_err(&pdev->dev, "3p3v: regulator not found: %ld."
, PTR_ERR(tegra->xusb_s3p3v_reg));
err = PTR_ERR(tegra->xusb_s3p3v_reg);
goto err_null_regulator;
} else {
err = regulator_enable(tegra->xusb_s3p3v_reg);
if (err < 0) {
dev_err(&pdev->dev,
"3p3v: regulator enable failed:%d\n", err);
goto err_null_regulator;
}
}
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) {
dev_err(&pdev->dev,
"5p0v: regulator enable failed:%d\n",
err);
goto err_put_s3p3v_reg;
}
}
}
tegra->xusb_s1p8v_reg =
devm_regulator_get(&pdev->dev, supply->s1p8v);
if (IS_ERR(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;
} 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;
}
}
tegra->xusb_s1p05v_reg =
devm_regulator_get(&pdev->dev, supply->s1p05v);
if (IS_ERR(tegra->xusb_s1p05v_reg)) {
dev_err(&pdev->dev, "1p05v: regulator not found: %ld."
, PTR_ERR(tegra->xusb_s1p05v_reg));
err = PTR_ERR(tegra->xusb_s1p05v_reg);
goto err_put_s1p8v_reg;
} else {
err = regulator_enable(tegra->xusb_s1p05v_reg);
if (err < 0) {
dev_err(&pdev->dev,
"1p05v: regulator enable failed:%d\n", err);
goto err_put_s1p8v_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:
regulator_disable(tegra->xusb_s3p3v_reg);
err_null_regulator:
tegra->xusb_s5p0v_reg = NULL;
tegra->xusb_s5p0v1_reg = NULL;
tegra->xusb_s5p0v2_reg = NULL;
tegra->xusb_s1p05v_reg = NULL;
tegra->xusb_s3p3v_reg = NULL;
tegra->xusb_s1p8v_reg = NULL;
return err;
}
static void tegra_xusb_regulator_deinit(struct tegra_xhci_hcd *tegra)
{
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);
}
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;
}
/*
* We need to enable only plle_clk as pllu_clk, utmip_clk and plle_re_vco_clk
* are under hardware control
*/
static int tegra_usb2_clocks_init(struct tegra_xhci_hcd *tegra)
{
struct platform_device *pdev = tegra->pdev;
int err = 0;
tegra->plle_clk = devm_clk_get(&pdev->dev, "pll_e");
if (IS_ERR(tegra->plle_clk)) {
dev_err(&pdev->dev, "%s: Failed to get plle clock\n", __func__);
err = PTR_ERR(tegra->plle_clk);
return err;
}
err = clk_enable(tegra->plle_clk);
if (err) {
dev_err(&pdev->dev, "%s: could not enable plle clock\n",
__func__);
return err;
}
return err;
}
static void tegra_usb2_clocks_deinit(struct tegra_xhci_hcd *tegra)
{
clk_disable(tegra->plle_clk);
tegra->plle_clk = NULL;
}
static int tegra_xusb_partitions_clk_init(struct tegra_xhci_hcd *tegra)
{
struct platform_device *pdev = tegra->pdev;
int err = 0;
tegra->emc_clk = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clk)) {
dev_err(&pdev->dev, "Failed to get xusb.emc clock\n");
return PTR_ERR(tegra->emc_clk);
}
if (tegra->pdata->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");
err = PTR_ERR(tegra->pll_re_vco_clk);
goto get_pll_re_vco_clk_failed;
}
}
/* get the clock handle of 120MHz clock source */
tegra->pll_u_480M = devm_clk_get(&pdev->dev, "pll_u_480M");
if (IS_ERR(tegra->pll_u_480M)) {
dev_err(&pdev->dev, "Failed to get pll_u_480M clk handle\n");
err = PTR_ERR(tegra->pll_u_480M);
goto get_pll_u_480M_failed;
}
/* get the clock handle of 12MHz clock source */
tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m");
if (IS_ERR(tegra->clk_m)) {
dev_err(&pdev->dev, "Failed to get clk_m clk handle\n");
err = PTR_ERR(tegra->clk_m);
goto clk_get_clk_m_failed;
}
tegra->ss_src_clk = devm_clk_get(&pdev->dev, "ss_src");
if (IS_ERR(tegra->ss_src_clk)) {
dev_err(&pdev->dev, "Failed to get SSPI clk\n");
err = PTR_ERR(tegra->ss_src_clk);
tegra->ss_src_clk = NULL;
goto get_ss_src_clk_failed;
}
tegra->host_clk = devm_clk_get(&pdev->dev, "host");
if (IS_ERR(tegra->host_clk)) {
dev_err(&pdev->dev, "Failed to get host partition clk\n");
err = PTR_ERR(tegra->host_clk);
tegra->host_clk = NULL;
goto get_host_clk_failed;
}
tegra->ss_clk = devm_clk_get(&pdev->dev, "ss");
if (IS_ERR(tegra->ss_clk)) {
dev_err(&pdev->dev, "Failed to get ss partition clk\n");
err = PTR_ERR(tegra->ss_clk);
tegra->ss_clk = NULL;
goto get_ss_clk_failed;
}
if (tegra->pdata->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");
goto enable_pll_re_vco_clk_failed;
}
}
return 0;
enable_pll_re_vco_clk_failed:
tegra->ss_clk = NULL;
get_ss_clk_failed:
tegra->host_clk = NULL;
get_host_clk_failed:
tegra->ss_src_clk = NULL;
get_ss_src_clk_failed:
tegra->clk_m = NULL;
clk_get_clk_m_failed:
tegra->pll_u_480M = NULL;
get_pll_u_480M_failed:
if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
tegra->pll_re_vco_clk = NULL;
get_pll_re_vco_clk_failed:
tegra->emc_clk = NULL;
return err;
}
static void tegra_xusb_partitions_clk_deinit(struct tegra_xhci_hcd *tegra)
{
clk_disable(tegra->ss_clk);
clk_disable(tegra->host_clk);
if (tegra->pdata->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)
tegra->pll_re_vco_clk = NULL;
}
static void tegra_xhci_rx_idle_mode_override(struct tegra_xhci_hcd *tegra,
bool enable)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg;
/* Issue is only applicable for T114 */
if (XUSB_DEVICE_ID_T114 != tegra->device_id)
return;
if (tegra->bdata->portmap & TEGRA_XUSB_SS_P0) {
reg = readl(tegra->padctl_base +
padregs->iophy_misc_pad_p0_ctl3_0);
if (enable) {
reg &= ~RX_IDLE_MODE;
reg |= RX_IDLE_MODE_OVRD;
} else {
reg |= RX_IDLE_MODE;
reg &= ~RX_IDLE_MODE_OVRD;
}
writel(reg, tegra->padctl_base +
padregs->iophy_misc_pad_p0_ctl3_0);
}
if (tegra->bdata->portmap & TEGRA_XUSB_SS_P1) {
reg = readl(tegra->padctl_base +
padregs->iophy_misc_pad_p1_ctl3_0);
if (enable) {
reg &= ~RX_IDLE_MODE;
reg |= RX_IDLE_MODE_OVRD;
} else {
reg |= RX_IDLE_MODE;
reg &= ~RX_IDLE_MODE_OVRD;
}
writel(reg, tegra->padctl_base +
padregs->iophy_misc_pad_p1_ctl3_0);
/* SATA lane also if USB3_SS port1 mapped to it */
if (XUSB_DEVICE_ID_T114 != tegra->device_id &&
tegra->bdata->lane_owner & BIT(0)) {
reg = readl(tegra->padctl_base +
padregs->iophy_misc_pad_s0_ctl3_0);
if (enable) {
reg &= ~RX_IDLE_MODE;
reg |= RX_IDLE_MODE_OVRD;
} else {
reg |= RX_IDLE_MODE;
reg &= ~RX_IDLE_MODE_OVRD;
}
writel(reg, tegra->padctl_base +
padregs->iophy_misc_pad_s0_ctl3_0);
}
}
}
/* Enable ss clk, host clk, falcon clk,
* fs clk, dev clk, plle and refplle
*/
static int
tegra_xusb_request_clk_rate(struct tegra_xhci_hcd *tegra,
struct clk *clk_handle, u32 rate, u32 *sw_resp)
{
int ret = 0;
enum MBOX_CMD_TYPE cmd_ack = MBOX_CMD_ACK;
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 = CMD_DATA(fw_req_rate) | CMD_TYPE(MBOX_CMD_ACK);
return ret;
}
/* frequency request from firmware is in KHz.
* Convert it to MHz
*/
/* get current rate of clock */
cur_rate = clk_get_rate(clk_handle);
cur_rate /= 1000;
if (fw_req_rate == cur_rate) {
cmd_ack = MBOX_CMD_ACK;
} else {
if (clk_handle == tegra->ss_src_clk && fw_req_rate == 12000) {
/* Change SS clock source to CLK_M at 12MHz */
clk_set_parent(clk_handle, tegra->clk_m);
clk_set_rate(clk_handle, fw_req_rate * 1000);
/* save leakage power when SS freq is being decreased */
tegra_xhci_rx_idle_mode_override(tegra, true);
} else if (clk_handle == tegra->ss_src_clk &&
fw_req_rate == 120000) {
/* Change SS clock source to HSIC_480 at 120MHz */
clk_set_rate(clk_handle, 3000 * 1000);
clk_set_parent(clk_handle, tegra->pll_u_480M);
/* clear ovrd bits when SS freq is being increased */
tegra_xhci_rx_idle_mode_override(tegra, false);
}
cur_rate = (clk_get_rate(clk_handle) / 1000);
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_DATA(cur_rate) | CMD_TYPE(cmd_ack);
return ret;
}
static void tegra_xhci_save_dfe_context(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct xhci_hcd *xhci = tegra->xhci;
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 offset;
u32 reg;
if (port > (XUSB_SS_PORT_COUNT - 1)) {
pr_err("%s invalid SS port number %u\n", __func__, port);
return;
}
xhci_info(xhci, "saving restore DFE context for port %d\n", port);
/* if port1 is mapped to SATA lane then read from SATA register */
if (port == 1 && XUSB_DEVICE_ID_T114 != tegra->device_id &&
tegra->bdata->lane_owner & BIT(0))
offset = padregs->iophy_misc_pad_s0_ctl6_0;
else
offset = MISC_PAD_CTL_6_0(port);
/*
* Value set to IOPHY_MISC_PAD_x_CTL_6 where x P0/P1/S0/ is from,
* T114 refer PG USB3_FW_Programming_Guide_Host.doc section 14.3.10
* T124 refer PG T124_USB3_FW_Programming_Guide_Host.doc section 14.3.10
*/
reg = readl(tegra->padctl_base + offset);
reg &= ~MISC_OUT_SEL(~0);
reg |= MISC_OUT_SEL(0x32);
writel(reg, tegra->padctl_base + offset);
reg = readl(tegra->padctl_base + offset);
tegra->sregs.tap1_val[port] = MISC_OUT_TAP_VAL(reg);
reg = readl(tegra->padctl_base + offset);
reg &= ~MISC_OUT_SEL(~0);
reg |= MISC_OUT_SEL(0x33);
writel(reg, tegra->padctl_base + offset);
reg = readl(tegra->padctl_base + offset);
tegra->sregs.amp_val[port] = MISC_OUT_AMP_VAL(reg);
reg = readl(tegra->padctl_base + USB3_PAD_CTL_4_0(port));
reg &= ~DFE_CNTL_TAP_VAL(~0);
reg |= DFE_CNTL_TAP_VAL(tegra->sregs.tap1_val[port]);
writel(reg, tegra->padctl_base + USB3_PAD_CTL_4_0(port));
reg = readl(tegra->padctl_base + USB3_PAD_CTL_4_0(port));
reg &= ~DFE_CNTL_AMP_VAL(~0);
reg |= DFE_CNTL_AMP_VAL(tegra->sregs.amp_val[port]);
writel(reg, tegra->padctl_base + USB3_PAD_CTL_4_0(port));
tegra->dfe_ctx_saved[port] = true;
}
static void save_ctle_context(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct xhci_hcd *xhci = tegra->xhci;
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 offset;
u32 reg;
if (port > (XUSB_SS_PORT_COUNT - 1)) {
pr_err("%s invalid SS port number %u\n", __func__, port);
return;
}
xhci_info(xhci, "saving restore CTLE context for port %d\n", port);
/* if port1 is mapped to SATA lane then read from SATA register */
if (port == 1 && XUSB_DEVICE_ID_T114 != tegra->device_id &&
tegra->bdata->lane_owner & BIT(0))
offset = padregs->iophy_misc_pad_s0_ctl6_0;
else
offset = MISC_PAD_CTL_6_0(port);
/*
* Value set to IOPHY_MISC_PAD_x_CTL_6 where x P0/P1/S0/ is from,
* T114 refer PG USB3_FW_Programming_Guide_Host.doc section 14.3.10
* T124 refer PG T124_USB3_FW_Programming_Guide_Host.doc section 14.3.10
*/
reg = readl(tegra->padctl_base + offset);
reg &= ~MISC_OUT_SEL(~0);
reg |= MISC_OUT_SEL(0xa1);
writel(reg, tegra->padctl_base + offset);
reg = readl(tegra->padctl_base + offset);
reg &= ~MISC_OUT_SEL(~0);
reg |= MISC_OUT_SEL(0x21);
writel(reg, tegra->padctl_base + offset);
reg = readl(tegra->padctl_base + offset);
tegra->sregs.ctle_g_val[port] = MISC_OUT_G_Z_VAL(reg);
reg = readl(tegra->padctl_base + offset);
reg &= ~MISC_OUT_SEL(~0);
reg |= MISC_OUT_SEL(0x48);
writel(reg, tegra->padctl_base + offset);
reg = readl(tegra->padctl_base + offset);
tegra->sregs.ctle_z_val[port] = MISC_OUT_G_Z_VAL(reg);
reg = readl(tegra->padctl_base + USB3_PAD_CTL_2_0(port));
reg &= ~RX_EQ_Z_VAL(~0);
reg |= RX_EQ_Z_VAL(tegra->sregs.ctle_z_val[port]);
writel(reg, tegra->padctl_base + USB3_PAD_CTL_2_0(port));
reg = readl(tegra->padctl_base + USB3_PAD_CTL_2_0(port));
reg &= ~RX_EQ_G_VAL(~0);
reg |= RX_EQ_G_VAL(tegra->sregs.ctle_g_val[port]);
writel(reg, tegra->padctl_base + USB3_PAD_CTL_2_0(port));
tegra->ctle_ctx_saved[port] = true;
}
static void tegra_xhci_restore_dfe_context(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct xhci_hcd *xhci = tegra->xhci;
u32 reg;
/* don't restore if not saved */
if (tegra->dfe_ctx_saved[port] == false)
return;
xhci_info(xhci, "restoring dfe context of port %d\n", port);
/* restore dfe_cntl for the port */
reg = readl(tegra->padctl_base + USB3_PAD_CTL_4_0(port));
reg &= ~(DFE_CNTL_AMP_VAL(~0) |
DFE_CNTL_TAP_VAL(~0));
reg |= DFE_CNTL_AMP_VAL(tegra->sregs.amp_val[port]) |
DFE_CNTL_TAP_VAL(tegra->sregs.tap1_val[port]);
writel(reg, tegra->padctl_base + USB3_PAD_CTL_4_0(port));
}
void restore_ctle_context(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct xhci_hcd *xhci = tegra->xhci;
u32 reg;
/* don't restore if not saved */
if (tegra->ctle_ctx_saved[port] == false)
return;
xhci_info(xhci, "restoring CTLE context of port %d\n", port);
/* restore ctle for the port */
reg = readl(tegra->padctl_base + USB3_PAD_CTL_2_0(port));
reg &= ~(RX_EQ_Z_VAL(~0) |
RX_EQ_G_VAL(~0));
reg |= (RX_EQ_Z_VAL(tegra->sregs.ctle_z_val[port]) |
RX_EQ_G_VAL(tegra->sregs.ctle_g_val[port]));
writel(reg, tegra->padctl_base + USB3_PAD_CTL_2_0(port));
}
static void tegra_xhci_program_ulpi_pad(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg;
reg = readl(tegra->padctl_base + padregs->usb2_pad_mux_0);
reg &= ~USB2_ULPI_PAD;
reg |= USB2_ULPI_PAD_OWNER_XUSB;
writel(reg, tegra->padctl_base + padregs->usb2_pad_mux_0);
reg = readl(tegra->padctl_base + padregs->usb2_port_cap_0);
reg &= ~USB2_ULPI_PORT_CAP;
reg |= (tegra->bdata->ulpicap << 24);
writel(reg, tegra->padctl_base + padregs->usb2_port_cap_0);
/* FIXME: Program below when more details available
* XUSB_PADCTL_ULPI_LINK_TRIM_CONTROL_0
* XUSB_PADCTL_ULPI_NULL_CLK_TRIM_CONTROL_0
*/
}
static void tegra_xhci_program_utmip_pad(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg;
u32 ctl0_offset, ctl1_offset;
reg = readl(tegra->padctl_base + padregs->usb2_pad_mux_0);
reg &= ~USB2_OTG_PAD_PORT_MASK(port);
reg |= USB2_OTG_PAD_PORT_OWNER_XUSB(port);
writel(reg, tegra->padctl_base + padregs->usb2_pad_mux_0);
reg = readl(tegra->padctl_base + padregs->usb2_port_cap_0);
reg &= ~USB2_PORT_CAP_MASK(port);
reg |= USB2_PORT_CAP_HOST(port);
writel(reg, tegra->padctl_base + padregs->usb2_port_cap_0);
/*
* Modify only the bits which belongs to the port
* and enable respective VBUS_PAD for the port
*/
if (tegra->bdata->uses_external_pmic == false) {
reg = readl(tegra->padctl_base + padregs->oc_det_0);
reg &= ~(port == 2 ? OC_DET_VBUS_ENABLE2_OC_MAP :
port ? OC_DET_VBUS_ENABLE1_OC_MAP :
OC_DET_VBUS_ENABLE0_OC_MAP);
reg |= (port == 2) ? OC_DET_VBUS_EN2_OC_DETECTED_VBUS_PAD2 :
port ? OC_DET_VBUS_EN1_OC_DETECTED_VBUS_PAD1 :
OC_DET_VBUS_EN0_OC_DETECTED_VBUS_PAD0;
writel(reg, tegra->padctl_base + padregs->oc_det_0);
}
/*
* enable respective VBUS_PAD if port is mapped to any SS port
*/
reg = readl(tegra->padctl_base + padregs->usb2_oc_map_0);
reg &= ~((port == 2) ? USB2_OC_MAP_PORT2 :
port ? USB2_OC_MAP_PORT1 : USB2_OC_MAP_PORT0);
reg |= (0x4 | port) << (port * 3);
writel(reg, tegra->padctl_base + padregs->usb2_oc_map_0);
ctl0_offset = (port == 2) ? padregs->usb2_otg_pad2_ctl0_0 :
port ? padregs->usb2_otg_pad1_ctl0_0 :
padregs->usb2_otg_pad0_ctl0_0;
ctl1_offset = (port == 2) ? padregs->usb2_otg_pad2_ctl1_0 :
port ? padregs->usb2_otg_pad1_ctl1_0 :
padregs->usb2_otg_pad0_ctl1_0;
reg = readl(tegra->padctl_base + ctl0_offset);
reg &= ~(USB2_OTG_HS_CURR_LVL | USB2_OTG_HS_SLEW |
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;
writel(reg, tegra->padctl_base + ctl0_offset);
reg = readl(tegra->padctl_base + ctl1_offset);
reg &= ~(USB2_OTG_TERM_RANGE_AD | USB2_OTG_HS_IREF_CAP
| 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);
writel(reg, tegra->padctl_base + ctl1_offset);
/*Release OTG port if not in host mode*/
if ((port == 0) && !is_otg_host(tegra))
tegra_xhci_release_otg_port(true);
}
static void tegra_xhci_program_ss_pad(struct tegra_xhci_hcd *tegra,
u8 port)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 ctl2_offset, ctl4_offset, ctl5_offset;
u32 reg;
ctl2_offset = port ? padregs->iophy_usb3_pad1_ctl2_0 :
padregs->iophy_usb3_pad0_ctl2_0;
ctl4_offset = port ? padregs->iophy_usb3_pad1_ctl4_0 :
padregs->iophy_usb3_pad0_ctl4_0;
ctl5_offset = port ? padregs->iophy_misc_pad_p1_ctl5_0 :
padregs->iophy_misc_pad_p0_ctl5_0;
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;
writel(reg, tegra->padctl_base + ctl2_offset);
reg = readl(tegra->padctl_base + ctl4_offset);
reg = tegra->pdata->dfe_cntl;
writel(reg, tegra->padctl_base + ctl4_offset);
reg = readl(tegra->padctl_base + ctl5_offset);
reg |= RX_QEYE_EN;
writel(reg, 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);
writel(reg, tegra->padctl_base + MISC_PAD_CTL_2_0(port));
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);
tegra_xhci_restore_dfe_context(tegra, port);
tegra_xhci_restore_ctle_context(tegra, port);
}
/* This function assigns the USB ports to the controllers,
* then programs the port capabilities and pad parameters
* of ports assigned to XUSB after booted to OS.
*/
void
tegra_xhci_padctl_portmap_and_caps(struct tegra_xhci_hcd *tegra)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg, oc_bits = 0;
unsigned pad;
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;
writel(reg, tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
reg = readl(tegra->padctl_base + padregs->snps_oc_map_0);
reg |= SNPS_OC_MAP_CTRL1 | SNPS_OC_MAP_CTRL2 | SNPS_OC_MAP_CTRL3;
writel(reg, tegra->padctl_base + padregs->snps_oc_map_0);
reg = readl(tegra->padctl_base + padregs->snps_oc_map_0);
reg = readl(tegra->padctl_base + padregs->oc_det_0);
reg |= OC_DET_VBUS_ENABLE0_OC_MAP | OC_DET_VBUS_ENABLE1_OC_MAP;
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
reg |= OC_DET_VBUS_ENABLE2_OC_MAP;
writel(reg, tegra->padctl_base + padregs->oc_det_0);
/* check if over current seen. Clear if present */
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P0)
oc_bits |= OC_DET_OC_DETECTED_VBUS_PAD0;
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
oc_bits |= OC_DET_OC_DETECTED_VBUS_PAD1;
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
oc_bits |= OC_DET_OC_DETECTED_VBUS_PAD2;
reg = readl(tegra->padctl_base + padregs->oc_det_0);
if (reg & oc_bits) {
xhci_info(tegra->xhci, "Over current detected. Clearing...\n");
writel(reg, tegra->padctl_base + padregs->oc_det_0);
usleep_range(100, 200);
reg = readl(tegra->padctl_base + padregs->oc_det_0);
if (reg & oc_bits)
xhci_info(tegra->xhci, "Over current still present\n");
}
reg = readl(tegra->padctl_base + padregs->usb2_oc_map_0);
reg = USB2_OC_MAP_PORT0 | USB2_OC_MAP_PORT1;
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
reg |= USB2_OC_MAP_PORT2;
writel(reg, tegra->padctl_base + padregs->usb2_oc_map_0);
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P0)
tegra_xhci_program_utmip_pad(tegra, 0);
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
tegra_xhci_program_utmip_pad(tegra, 1);
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
tegra_xhci_program_utmip_pad(tegra, 2);
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);
if (tegra->bdata->portmap & TEGRA_XUSB_SS_P0) {
tegra_xhci_program_ss_pad(tegra, 0);
} else {
/* set rx_idle_mode_ovrd for unused SS ports to save power */
reg = readl(tegra->padctl_base +
padregs->iophy_misc_pad_p0_ctl3_0);
reg &= ~RX_IDLE_MODE;
reg |= RX_IDLE_MODE_OVRD;
writel(reg, tegra->padctl_base +
padregs->iophy_misc_pad_p0_ctl3_0);
}
if (tegra->bdata->portmap & TEGRA_XUSB_SS_P1) {
tegra_xhci_program_ss_pad(tegra, 1);
} else {
/* set rx_idle_mode_ovrd for unused SS ports to save power */
reg = readl(tegra->padctl_base +
padregs->iophy_misc_pad_p1_ctl3_0);
reg &= ~RX_IDLE_MODE;
reg |= RX_IDLE_MODE_OVRD;
writel(reg, tegra->padctl_base +
padregs->iophy_misc_pad_p1_ctl3_0);
/* SATA lane also if USB3_SS port1 mapped to it but unused */
if (XUSB_DEVICE_ID_T114 != tegra->device_id &&
tegra->bdata->lane_owner & BIT(0)) {
reg = readl(tegra->padctl_base +
padregs->iophy_misc_pad_s0_ctl3_0);
reg &= ~RX_IDLE_MODE;
reg |= RX_IDLE_MODE_OVRD;
writel(reg, tegra->padctl_base +
padregs->iophy_misc_pad_s0_ctl3_0);
}
}
if (XUSB_DEVICE_ID_T114 != tegra->device_id) {
tegra_xhci_setup_gpio_for_ss_lane(tegra);
usb3_phy_pad_enable(tegra->bdata->lane_owner);
}
}
/* This function read XUSB registers and stores in device context */
static void
tegra_xhci_save_xusb_ctx(struct tegra_xhci_hcd *tegra)
{
/* a. Save the IPFS registers */
tegra->sregs.msi_bar_sz =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_MSI_BAR_SZ_0);
tegra->sregs.msi_axi_barst =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0);
tegra->sregs.msi_fpci_barst =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_FPCI_BAR_ST_0);
tegra->sregs.msi_vec0 =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_MSI_VEC0_0);
tegra->sregs.msi_en_vec0 =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_MSI_EN_VEC0_0);
tegra->sregs.fpci_error_masks =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0);
tegra->sregs.intr_mask =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_INTR_MASK_0);
tegra->sregs.ipfs_intr_enable =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_IPFS_INTR_ENABLE_0);
tegra->sregs.ufpci_config =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_UFPCI_CONFIG_0);
tegra->sregs.clkgate_hysteresis =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
tegra->sregs.xusb_host_mccif_fifo_cntrl =
readl(tegra->ipfs_base + IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0);
/* b. Save the CFG registers */
tegra->sregs.hs_pls =
readl(tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_HS_PLS);
tegra->sregs.fs_pls =
readl(tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_FS_PLS);
tegra->sregs.hs_fs_speed =
readl(tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_HSFS_SPEED);
tegra->sregs.hs_fs_pp =
readl(tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_HSFS_PP);
tegra->sregs.cfg_aru =
readl(tegra->fpci_base + XUSB_CFG_ARU_CONTEXT);
tegra->sregs.cfg_order =
readl(tegra->fpci_base + XUSB_CFG_FPCICFG);
tegra->sregs.cfg_fladj =
readl(tegra->fpci_base + XUSB_CFG_24);
tegra->sregs.cfg_sid =
readl(tegra->fpci_base + XUSB_CFG_16);
}
/* This function restores XUSB registers from device context */
static void
tegra_xhci_restore_ctx(struct tegra_xhci_hcd *tegra)
{
/* Restore Cfg registers */
writel(tegra->sregs.hs_pls,
tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_HS_PLS);
writel(tegra->sregs.fs_pls,
tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_FS_PLS);
writel(tegra->sregs.hs_fs_speed,
tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_HSFS_SPEED);
writel(tegra->sregs.hs_fs_pp,
tegra->fpci_base + XUSB_CFG_ARU_CONTEXT_HSFS_PP);
writel(tegra->sregs.cfg_aru,
tegra->fpci_base + XUSB_CFG_ARU_CONTEXT);
writel(tegra->sregs.cfg_order,
tegra->fpci_base + XUSB_CFG_FPCICFG);
writel(tegra->sregs.cfg_fladj,
tegra->fpci_base + XUSB_CFG_24);
writel(tegra->sregs.cfg_sid,
tegra->fpci_base + XUSB_CFG_16);
/* Restore IPFS registers */
writel(tegra->sregs.msi_bar_sz,
tegra->ipfs_base + IPFS_XUSB_HOST_MSI_BAR_SZ_0);
writel(tegra->sregs.msi_axi_barst,
tegra->ipfs_base + IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0);
writel(tegra->sregs.msi_fpci_barst,
tegra->ipfs_base + IPFS_XUSB_HOST_FPCI_BAR_ST_0);
writel(tegra->sregs.msi_vec0,
tegra->ipfs_base + IPFS_XUSB_HOST_MSI_VEC0_0);
writel(tegra->sregs.msi_en_vec0,
tegra->ipfs_base + IPFS_XUSB_HOST_MSI_EN_VEC0_0);
writel(tegra->sregs.fpci_error_masks,
tegra->ipfs_base + IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0);
writel(tegra->sregs.intr_mask,
tegra->ipfs_base + IPFS_XUSB_HOST_INTR_MASK_0);
writel(tegra->sregs.ipfs_intr_enable,
tegra->ipfs_base + IPFS_XUSB_HOST_IPFS_INTR_ENABLE_0);
writel(tegra->sregs.ufpci_config,
tegra->fpci_base + IPFS_XUSB_HOST_UFPCI_CONFIG_0);
writel(tegra->sregs.clkgate_hysteresis,
tegra->ipfs_base + IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
writel(tegra->sregs.xusb_host_mccif_fifo_cntrl,
tegra->ipfs_base + IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0);
}
static void tegra_xhci_enable_fw_message(struct tegra_xhci_hcd *tegra)
{
fw_message_send(tegra, MBOX_CMD_MSG_ENABLED, 0 /* no data needed */);
}
static int load_firmware(struct tegra_xhci_hcd *tegra, bool resetARU)
{
struct platform_device *pdev = tegra->pdev;
struct cfgtbl *cfg_tbl = (struct cfgtbl *) tegra->firmware.data;
u32 phys_addr_lo;
u32 HwReg;
u16 nblocks;
time_t fw_time;
struct tm fw_tm;
u8 hc_caplength;
u32 usbsts, count = 0xff;
struct xhci_cap_regs __iomem *cap_regs;
struct xhci_op_regs __iomem *op_regs;
/* enable mbox interrupt */
writel(readl(tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD) | MBOX_INT_EN,
tegra->fpci_base + XUSB_CFG_ARU_MBOX_CMD);
/* First thing, reset the ARU. By the time we get to
* loading boot code below, reset would be complete.
* alternatively we can busy wait on rst pending bit.
*/
/* Don't reset during ELPG/LP0 exit path */
if (resetARU) {
iowrite32(0x1, tegra->fpci_base + XUSB_CFG_ARU_RST);
usleep_range(1000, 2000);
}
if (csb_read(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
dev_info(&pdev->dev, "Firmware already loaded, Falcon state 0x%x\n",
csb_read(tegra, XUSB_FALC_CPUCTL));
return 0;
}
/* update the phys_log_buffer and total_entries here */
cfg_tbl->phys_addr_log_buffer = tegra->log.phys_addr;
cfg_tbl->total_log_entries = FW_LOG_COUNT;
phys_addr_lo = tegra->firmware.dma;
phys_addr_lo += sizeof(struct cfgtbl);
/* Program the size of DFI into ILOAD_ATTR */
csb_write(tegra, XUSB_CSB_MP_ILOAD_ATTR, tegra->firmware.size);
/* Boot code of the firmware reads the ILOAD_BASE_LO register
* to get to the start of the dfi in system memory.
*/
csb_write(tegra, XUSB_CSB_MP_ILOAD_BASE_LO, phys_addr_lo);
/* Program the ILOAD_BASE_HI with a value of MSB 32 bits */
csb_write(tegra, XUSB_CSB_MP_ILOAD_BASE_HI, 0);
/* Set BOOTPATH to 1 in APMAP Register. Bit 31 is APMAP_BOOTMAP */
csb_write(tegra, XUSB_CSB_MP_APMAP, APMAP_BOOTPATH);
/* Invalidate L2IMEM. */
csb_write(tegra, XUSB_CSB_MP_L2IMEMOP_TRIG, L2IMEM_INVALIDATE_ALL);
/* Initiate fetch of Bootcode from system memory into L2IMEM.
* Program BootCode location and size in system memory.
*/
HwReg = ((cfg_tbl->boot_codetag / IMEM_BLOCK_SIZE) &
L2IMEMOP_SIZE_SRC_OFFSET_MASK)
<< L2IMEMOP_SIZE_SRC_OFFSET_SHIFT;
HwReg |= ((cfg_tbl->boot_codesize / IMEM_BLOCK_SIZE) &
L2IMEMOP_SIZE_SRC_COUNT_MASK)
<< L2IMEMOP_SIZE_SRC_COUNT_SHIFT;
csb_write(tegra, XUSB_CSB_MP_L2IMEMOP_SIZE, HwReg);
/* Trigger L2IMEM Load operation. */
csb_write(tegra, XUSB_CSB_MP_L2IMEMOP_TRIG, L2IMEM_LOAD_LOCKED_RESULT);
/* Setup Falcon Auto-fill */
nblocks = (cfg_tbl->boot_codesize / IMEM_BLOCK_SIZE);
if ((cfg_tbl->boot_codesize % IMEM_BLOCK_SIZE) != 0)
nblocks += 1;
csb_write(tegra, XUSB_FALC_IMFILLCTL, nblocks);
HwReg = (cfg_tbl->boot_codetag / IMEM_BLOCK_SIZE) & IMFILLRNG_TAG_MASK;
HwReg |= (((cfg_tbl->boot_codetag + cfg_tbl->boot_codesize)
/IMEM_BLOCK_SIZE) - 1) << IMFILLRNG1_TAG_HI_SHIFT;
csb_write(tegra, XUSB_FALC_IMFILLRNG1, HwReg);
csb_write(tegra, XUSB_FALC_DMACTL, 0);
msleep(50);
csb_write(tegra, XUSB_FALC_BOOTVEC, cfg_tbl->boot_codetag);
/* Start Falcon CPU */
csb_write(tegra, XUSB_FALC_CPUCTL, CPUCTL_STARTCPU);
usleep_range(1000, 2000);
fw_time = cfg_tbl->fwimg_created_time;
time_to_tm(fw_time, 0, &fw_tm);
dev_info(&pdev->dev,
"Firmware timestamp: %ld-%02d-%02d %02d:%02d:%02d UTC, "\
"Falcon state 0x%x\n", fw_tm.tm_year + 1900,
fw_tm.tm_mon + 1, fw_tm.tm_mday, fw_tm.tm_hour,
fw_tm.tm_min, fw_tm.tm_sec,
csb_read(tegra, XUSB_FALC_CPUCTL));
dev_dbg(&pdev->dev, "num_hsic_port %d\n", cfg_tbl->num_hsic_port);
/* return fail if firmware status is not good */
if (csb_read(tegra, XUSB_FALC_CPUCTL) == XUSB_FALC_STATE_HALTED)
return -EFAULT;
cap_regs = IO_ADDRESS(tegra->host_phy_base);
hc_caplength = HC_LENGTH(ioread32(&cap_regs->hc_capbase));
op_regs = IO_ADDRESS(tegra->host_phy_base + hc_caplength);
/* wait for USBSTS_CNR to get set */
do {
usbsts = ioread32(&op_regs->status);
} while ((usbsts & STS_CNR) && count--);
if (!count && (usbsts & STS_CNR)) {
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);
return 0;
}
static void tegra_xhci_release_port_ownership(struct tegra_xhci_hcd *tegra,
bool release)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg;
/* Issue is only applicable for T114 */
if (XUSB_DEVICE_ID_T114 != tegra->device_id)
return;
reg = readl(tegra->padctl_base + padregs->usb2_pad_mux_0);
reg &= ~(USB2_OTG_PAD_PORT_MASK(0) | USB2_OTG_PAD_PORT_MASK(1) |
USB2_OTG_PAD_PORT_MASK(2));
if (!release) {
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P0)
if (is_otg_host(tegra))
reg |= USB2_OTG_PAD_PORT_OWNER_XUSB(0);
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
reg |= USB2_OTG_PAD_PORT_OWNER_XUSB(1);
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
reg |= USB2_OTG_PAD_PORT_OWNER_XUSB(2);
}
writel(reg, tegra->padctl_base + padregs->usb2_pad_mux_0);
}
/* SS ELPG Entry initiated by fw */
static int tegra_xhci_ss_elpg_entry(struct tegra_xhci_hcd *tegra)
{
struct xhci_hcd *xhci = tegra->xhci;
u32 ret = 0;
must_have_sync_lock(tegra);
/* This is SS partition ELPG entry
* STEP 0: firmware will set WOC WOD bits in PVTPORTSC2 regs.
*/
/* Step 0: Acquire mbox and send PWRGATE msg to firmware
* only if it is sw initiated one
*/
/* STEP 1: xHCI firmware and xHCIPEP driver communicates
* SuperSpeed partition ELPG entry via mailbox protocol
*/
/* STEP 2: xHCI PEP driver and XUSB device mode driver
* enable the XUSB wakeup interrupts for the SuperSpeed
* and USB2.0 ports assigned to host.Section 4.1 Step 3
*/
tegra_xhci_ss_wake_on_interrupts(tegra->bdata->portmap, true);
/* STEP 3: xHCI PEP driver initiates the signal sequence
* to enable the XUSB SSwake detection logic for the
* SuperSpeed ports assigned to host.Section 4.1 Step 4
*/
tegra_xhci_ss_wake_signal(tegra->bdata->portmap, true);
/* STEP 4: System Power Management driver asserts reset
* to XUSB SuperSpeed partition then disables its clocks
*/
tegra_periph_reset_assert(tegra->ss_clk);
clk_disable(tegra->ss_clk);
usleep_range(100, 200);
/* STEP 5: System Power Management driver disables the
* XUSB SuperSpeed partition power rails.
*/
debug_print_portsc(xhci);
/* tegra_powergate_partition also does partition reset assert */
ret = tegra_powergate_partition(TEGRA_POWERGATE_XUSBA);
if (ret) {
xhci_err(xhci, "%s: could not powergate xusba partition\n",
__func__);
/* TODO: error recovery? */
}
tegra->ss_pwr_gated = true;
/* STEP 6: xHCI PEP driver initiates the signal sequence
* to enable the XUSB SSwake detection logic for the
* SuperSpeed ports assigned to host.Section 4.1 Step 7
*/
tegra_xhci_ss_vcore(tegra->bdata->portmap, true);
return ret;
}
/* Host ELPG Entry */
static int tegra_xhci_host_elpg_entry(struct tegra_xhci_hcd *tegra)
{
struct xhci_hcd *xhci = tegra->xhci;
u32 ret;
must_have_sync_lock(tegra);
/* If ss is already powergated skip ss ctx save stuff */
if (tegra->ss_pwr_gated) {
xhci_info(xhci, "%s: SS partition is already powergated\n",
__func__);
} else {
ret = tegra_xhci_ss_elpg_entry(tegra);
if (ret) {
xhci_err(xhci, "%s: ss_elpg_entry failed %d\n",
__func__, ret);
return ret;
}
}
/* 1. IS INTR PENDING INT_PENDING=1 ? */
/* STEP 1.1: Do a context save of XUSB and IPFS registers */
tegra_xhci_save_xusb_ctx(tegra);
/* calculate rctrl_val and tctrl_val */
tegra_xhci_war_for_tctrl_rctrl(tegra);
pmc_setup_wake_detect(tegra);
tegra_xhci_hs_wake_on_interrupts(tegra->bdata->portmap, true);
xhci_dbg(xhci, "%s: PMC_UTMIP_UHSIC_SLEEP_CFG_0 = %x\n", __func__,
tegra_usb_pmc_reg_read(PMC_UTMIP_UHSIC_SLEEP_CFG_0));
/* tegra_powergate_partition also does partition reset assert */
ret = tegra_powergate_partition(TEGRA_POWERGATE_XUSBC);
if (ret) {
xhci_err(xhci, "%s: could not unpowergate xusbc partition %d\n",
__func__, ret);
/* TODO: error handling? */
return ret;
}
tegra->host_pwr_gated = true;
clk_disable(tegra->host_clk);
if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_disable(tegra->pll_re_vco_clk);
clk_disable(tegra->emc_clk);
/* set port ownership to SNPS */
tegra_xhci_release_port_ownership(tegra, true);
xhci_dbg(xhci, "%s: PMC_UTMIP_UHSIC_SLEEP_CFG_0 = %x\n", __func__,
tegra_usb_pmc_reg_read(PMC_UTMIP_UHSIC_SLEEP_CFG_0));
xhci_info(xhci, "%s: elpg_entry: completed\n", __func__);
xhci_dbg(xhci, "%s: HOST POWER STATUS = %d\n",
__func__, tegra_powergate_is_powered(TEGRA_POWERGATE_XUSBC));
return ret;
}
/* SS ELPG Exit triggered by PADCTL irq */
/**
* tegra_xhci_ss_partition_elpg_exit - bring XUSBA partition out from elpg
*
* This function must be called with tegra->sync_lock acquired.
*
* @tegra: xhci controller context
* @return 0 for success, or error numbers
*/
static int tegra_xhci_ss_partition_elpg_exit(struct tegra_xhci_hcd *tegra)
{
struct xhci_hcd *xhci = tegra->xhci;
int ret = 0;
must_have_sync_lock(tegra);
if (tegra->ss_pwr_gated && (tegra->ss_wake_event ||
tegra->hs_wake_event || tegra->host_resume_req)) {
/*
* PWR_UNGATE SS partition. XUSBA
* tegra_unpowergate_partition also does partition reset
* deassert
*/
ret = tegra_unpowergate_partition(TEGRA_POWERGATE_XUSBA);
if (ret) {
xhci_err(xhci,
"%s: could not unpowergate xusba partition %d\n",
__func__, ret);
goto out;
}
if (tegra->ss_wake_event)
tegra->ss_wake_event = false;
} else {
xhci_info(xhci, "%s: ss already power gated\n",
__func__);
return ret;
}
/* Step 3: Enable clock to ss partition */
clk_enable(tegra->ss_clk);
/* Step 4: Disable ss wake detection logic */
tegra_xhci_ss_wake_on_interrupts(tegra->bdata->portmap, false);
/* Step 4.1: Disable ss wake detection logic */
tegra_xhci_ss_vcore(tegra->bdata->portmap, false);
/* wait 150us */
usleep_range(150, 200);
/* Step 4.2: Disable ss wake detection logic */
tegra_xhci_ss_wake_signal(tegra->bdata->portmap, false);
/* Step 6 Deassert reset for ss clks */
tegra_periph_reset_deassert(tegra->ss_clk);
xhci_dbg(xhci, "%s: SS ELPG EXIT. ALL DONE\n", __func__);
tegra->ss_pwr_gated = false;
out:
return ret;
}
static void ss_partition_elpg_exit_work(struct work_struct *work)
{
struct tegra_xhci_hcd *tegra = container_of(work, struct tegra_xhci_hcd,
ss_elpg_exit_work);
mutex_lock(&tegra->sync_lock);
tegra_xhci_ss_partition_elpg_exit(tegra);
mutex_unlock(&tegra->sync_lock);
}
/* read pmc WAKE2_STATUS register to know if SS port caused remote wake */
static void update_remote_wakeup_ports(struct tegra_xhci_hcd *tegra)
{
struct xhci_hcd *xhci = tegra->xhci;
u32 wake2_status;
int port;
#define PMC_WAKE2_STATUS 0x168
#define PADCTL_WAKE (1 << (58 - 32)) /* PADCTL is WAKE#58 */
wake2_status = tegra_usb_pmc_reg_read(PMC_WAKE2_STATUS);
if (wake2_status & PADCTL_WAKE) {
/* FIXME: This is customized for Dalmore, find a generic way */
set_bit(0, &tegra->usb3_rh_remote_wakeup_ports);
/* clear wake status */
tegra_usb_pmc_reg_write(PMC_WAKE2_STATUS, PADCTL_WAKE);
}
/* set all usb2 ports with RESUME link state as wakup ports */
for (port = 0; port < xhci->num_usb2_ports; port++) {
u32 portsc = xhci_readl(xhci, xhci->usb2_ports[port]);
if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
set_bit(port, &tegra->usb2_rh_remote_wakeup_ports);
}
xhci_dbg(xhci, "%s: usb2 roothub remote_wakeup_ports 0x%lx\n",
__func__, tegra->usb2_rh_remote_wakeup_ports);
xhci_dbg(xhci, "%s: usb3 roothub remote_wakeup_ports 0x%lx\n",
__func__, tegra->usb3_rh_remote_wakeup_ports);
}
static void wait_remote_wakeup_ports(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
int port, num_ports;
unsigned long *remote_wakeup_ports;
u32 portsc;
__le32 __iomem **port_array;
unsigned char *rh;
unsigned int retry = 64;
struct xhci_bus_state *bus_state;
bus_state = &xhci->bus_state[hcd_index(hcd)];
if (hcd == xhci->shared_hcd) {
port_array = xhci->usb3_ports;
num_ports = xhci->num_usb3_ports;
remote_wakeup_ports = &tegra->usb3_rh_remote_wakeup_ports;
rh = "usb3 roothub";
} else {
port_array = xhci->usb2_ports;
num_ports = xhci->num_usb2_ports;
remote_wakeup_ports = &tegra->usb2_rh_remote_wakeup_ports;
rh = "usb2 roothub";
}
while (*remote_wakeup_ports && retry--) {
for_each_set_bit(port, remote_wakeup_ports, num_ports) {
bool can_continue;
portsc = xhci_readl(xhci, port_array[port]);
if (!(portsc & PORT_CONNECT)) {
/* nothing to do if already disconnected */
clear_bit(port, remote_wakeup_ports);
continue;
}
if (hcd == xhci->shared_hcd) {
can_continue =
(portsc & PORT_PLS_MASK) == XDEV_U0;
} else {
unsigned long flags;
spin_lock_irqsave(&xhci->lock, flags);
can_continue =
test_bit(port, &bus_state->resuming_ports);
spin_unlock_irqrestore(&xhci->lock, flags);
}
if (can_continue)
clear_bit(port, remote_wakeup_ports);
else
xhci_dbg(xhci, "%s: %s port %d status 0x%x\n",
__func__, rh, port, portsc);
}
if (*remote_wakeup_ports)
msleep(20); /* give some time, irq will direct U0 */
}
xhci_dbg(xhci, "%s: %s remote_wakeup_ports 0x%lx\n", __func__, rh,
*remote_wakeup_ports);
}
static void tegra_xhci_war_for_tctrl_rctrl(struct tegra_xhci_hcd *tegra)
{
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) |
USB2_OTG_PAD_PORT_OWNER_XUSB(1);
if (XUSB_DEVICE_ID_T114 != tegra->device_id) {
portmux |= USB2_OTG_PAD_PORT_MASK(2);
portowner |= USB2_OTG_PAD_PORT_OWNER_XUSB(2);
}
/* Use xusb padctl space only when xusb owns all UTMIP port */
pad_mux = readl(tegra->padctl_base + padregs->usb2_pad_mux_0);
if ((pad_mux & portmux) == portowner) {
/* XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0::PD = 0 and
* XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0::PD_TRK = 0
*/
reg = readl(tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
reg &= ~((1 << 12) | (1 << 13));
writel(reg, tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
/* wait 20us */
usleep_range(20, 30);
/* Read XUSB_PADCTL:: XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_0
* :: TCTRL and RCTRL
*/
reg = readl(tegra->padctl_base + padregs->usb2_bias_pad_ctl1_0);
utmip_rctrl_val = RCTRL(reg);
utmip_tctrl_val = TCTRL(reg);
/*
* tctrl_val = 0x1f - (16 - ffz(utmip_tctrl_val)
* rctrl_val = 0x1f - (16 - ffz(utmip_rctrl_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
*/
reg = readl(tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
reg |= (1 << 13);
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
*/
} else {
/* Use common PMC API to use SNPS register space */
utmi_phy_set_snps_trking_data();
}
}
/* Host ELPG Exit triggered by PADCTL irq */
/**
* tegra_xhci_host_partition_elpg_exit - bring XUSBC partition out from elpg
*
* This function must be called with tegra->sync_lock acquired.
*
* @tegra: xhci controller context
* @return 0 for success, or error numbers
*/
static int
tegra_xhci_host_partition_elpg_exit(struct tegra_xhci_hcd *tegra)
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
struct xhci_hcd *xhci = tegra->xhci;
int ret = 0;
must_have_sync_lock(tegra);
if (!tegra->hc_in_elpg)
return 0;
clk_enable(tegra->emc_clk);
if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_enable(tegra->pll_re_vco_clk);
if (tegra->lp0_exit) {
u32 reg, oc_bits = 0;
/* Issue is only applicable for T114 */
if (XUSB_DEVICE_ID_T114 == tegra->device_id)
tegra_xhci_war_for_tctrl_rctrl(tegra);
/* check if over current seen. Clear if present */
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P0)
oc_bits |= OC_DET_OC_DETECTED_VBUS_PAD0;
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
oc_bits |= OC_DET_OC_DETECTED_VBUS_PAD1;
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
oc_bits |= OC_DET_OC_DETECTED_VBUS_PAD2;
reg = readl(tegra->padctl_base + padregs->oc_det_0);
xhci_dbg(xhci, "%s: oc_det_0=0x%x\n", __func__, reg);
if (reg & oc_bits) {
xhci_info(xhci, "Over current detected. Clearing...\n");
writel(reg, tegra->padctl_base + padregs->oc_det_0);
usleep_range(100, 200);
reg = readl(tegra->padctl_base + padregs->oc_det_0);
if (reg & oc_bits)
xhci_info(xhci, "Over current still present\n");
}
tegra_xhci_padctl_portmap_and_caps(tegra);
/* release clamps post deassert */
tegra->lp0_exit = false;
}
/* Clear FLUSH_ENABLE of MC client */
tegra_powergate_mc_flush_done(TEGRA_POWERGATE_XUSBC);
/* set port ownership back to xusb */
tegra_xhci_release_port_ownership(tegra, false);
/*
* PWR_UNGATE Host partition. XUSBC
* tegra_unpowergate_partition also does partition reset deassert
*/
ret = tegra_unpowergate_partition(TEGRA_POWERGATE_XUSBC);
if (ret) {
xhci_err(xhci, "%s: could not unpowergate xusbc partition %d\n",
__func__, ret);
goto out;
}
clk_enable(tegra->host_clk);
/* Step 4: Deassert reset to host partition clk */
tegra_periph_reset_deassert(tegra->host_clk);
/* Step 6.1: IPFS and XUSB BAR initialization */
tegra_xhci_cfg(tegra);
/* Step 6.2: IPFS and XUSB related restore */
tegra_xhci_restore_ctx(tegra);
/* Step 8: xhci spec related ctx restore
* will be done in xhci_resume().Do it here.
*/
tegra_xhci_ss_partition_elpg_exit(tegra);
/* Change SS clock source to HSIC_480 and set ss_src_clk at 120MHz */
if (clk_get_rate(tegra->ss_src_clk) == 12000000) {
clk_set_rate(tegra->ss_src_clk, 3000 * 1000);
clk_set_parent(tegra->ss_src_clk, tegra->pll_u_480M);
}
/* clear ovrd bits */
tegra_xhci_rx_idle_mode_override(tegra, false);
/* Load firmware */
xhci_dbg(xhci, "%s: elpg_exit: loading firmware from pmc.\n"
"ss (p1=0x%x, p2=0x%x, p3=0x%x), "
"hs (p1=0x%x, p2=0x%x, p3=0x%x),\n"
"fs (p1=0x%x, p2=0x%x, p3=0x%x)\n",
__func__,
csb_read(tegra, XUSB_FALC_SS_PVTPORTSC1),
csb_read(tegra, XUSB_FALC_SS_PVTPORTSC2),
csb_read(tegra, XUSB_FALC_SS_PVTPORTSC3),
csb_read(tegra, XUSB_FALC_HS_PVTPORTSC1),
csb_read(tegra, XUSB_FALC_HS_PVTPORTSC2),
csb_read(tegra, XUSB_FALC_HS_PVTPORTSC3),
csb_read(tegra, XUSB_FALC_FS_PVTPORTSC1),
csb_read(tegra, XUSB_FALC_FS_PVTPORTSC2),
csb_read(tegra, XUSB_FALC_FS_PVTPORTSC3));
debug_print_portsc(xhci);
ret = load_firmware(tegra, false /* EPLG exit, do not reset ARU */);
if (ret < 0) {
xhci_err(xhci, "%s: failed to load firmware %d\n",
__func__, ret);
goto out;
}
pmc_disable_bus_ctrl(tegra);
tegra->hc_in_elpg = false;
ret = xhci_resume(tegra->xhci, 0);
if (ret) {
xhci_err(xhci, "%s: could not resume right %d\n",
__func__, ret);
goto out;
}
update_remote_wakeup_ports(tegra);
if (tegra->hs_wake_event)
tegra->hs_wake_event = false;
if (tegra->host_resume_req)
tegra->host_resume_req = false;
xhci_info(xhci, "elpg_exit: completed: lp0/elpg time=%d msec\n",
jiffies_to_msecs(jiffies - tegra->last_jiffies));
tegra->host_pwr_gated = false;
out:
return ret;
}
static void host_partition_elpg_exit_work(struct work_struct *work)
{
struct tegra_xhci_hcd *tegra = container_of(work, struct tegra_xhci_hcd,
host_elpg_exit_work);
mutex_lock(&tegra->sync_lock);
tegra_xhci_host_partition_elpg_exit(tegra);
mutex_unlock(&tegra->sync_lock);
}
/* Mailbox handling function. This function handles requests
* from firmware and communicates with clock and powergating
* module to alter clock rates and to power gate/ungate xusb
* partitions.
*
* Following is the structure of mailbox messages.
* bit 31:28 - msg type
* bits 27:0 - mbox data
* FIXME: Check if we can just call clock functions like below
* or should we schedule it for calling later ?
*/
static void
tegra_xhci_process_mbox_message(struct work_struct *work)
{
u32 sw_resp = 0, cmd, data_in, fw_msg;
int ret = 0;
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;
mutex_lock(&tegra->mbox_lock);
/* 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 >> 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.
*/
switch (tegra->cmd_type) {
case MBOX_CMD_INC_FALC_CLOCK:
case MBOX_CMD_DEC_FALC_CLOCK:
ret = tegra_xusb_request_clk_rate(
tegra,
tegra->falc_clk,
tegra->cmd_data,
&sw_resp);
if (ret)
xhci_err(xhci, "%s: could not set required falc rate\n",
__func__);
goto send_sw_response;
case MBOX_CMD_INC_SSPI_CLOCK:
case MBOX_CMD_DEC_SSPI_CLOCK:
ret = tegra_xusb_request_clk_rate(
tegra,
tegra->ss_src_clk,
tegra->cmd_data,
&sw_resp);
if (ret)
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);
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 = CMD_DATA(tegra->cmd_data) | CMD_TYPE(MBOX_CMD_ACK);
goto send_sw_response;
case MBOX_CMD_STAR_HSIC_IDLE:
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);
mutex_unlock(&tegra->sync_lock);
if (ret)
break;
}
sw_resp = CMD_DATA(tegra->cmd_data);
if (!ret)
sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
else
sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
goto send_sw_response;
case MBOX_CMD_STOP_HSIC_IDLE:
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);
mutex_unlock(&tegra->sync_lock);
if (ret)
break;
}
sw_resp = CMD_DATA(tegra->cmd_data);
if (!ret)
sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
else
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);
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);
break;
default:
xhci_err(xhci, "%s: invalid cmdtype %d\n",
__func__, tegra->cmd_type);
}
mutex_unlock(&tegra->mbox_lock);
return;
send_sw_response:
if (((sw_resp >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK) == MBOX_CMD_NACK)
xhci_err(xhci, "%s respond fw message 0x%x with NAK\n",
__func__, fw_msg);
writel(sw_resp, 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);
}
static irqreturn_t pmc_usb_phy_wake_isr(int irq, void *data)
{
struct tegra_xhci_hcd *tegra = (struct tegra_xhci_hcd *) data;
struct xhci_hcd *xhci = tegra->xhci;
xhci_dbg(xhci, "%s irq %d", __func__, irq);
return IRQ_HANDLED;
}
static irqreturn_t tegra_xhci_padctl_irq(int irq, void *ptrdev)
{
struct tegra_xhci_hcd *tegra = (struct tegra_xhci_hcd *) ptrdev;
struct xhci_hcd *xhci = tegra->xhci;
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 elpg_program0 = 0;
spin_lock(&tegra->lock);
tegra->last_jiffies = jiffies;
/* Check the intr cause. Could be USB2 or HSIC or SS wake events */
elpg_program0 = tegra_usb_pad_reg_read(padregs->elpg_program_0);
/* Clear the interrupt cause. We already read the intr status. */
tegra_xhci_ss_wake_on_interrupts(tegra->bdata->portmap, false);
tegra_xhci_hs_wake_on_interrupts(tegra->bdata->portmap, false);
xhci_dbg(xhci, "%s: elpg_program0 = %x\n", __func__, elpg_program0);
xhci_dbg(xhci, "%s: PMC REGISTER = %x\n", __func__,
tegra_usb_pmc_reg_read(PMC_UTMIP_UHSIC_SLEEP_CFG_0));
xhci_dbg(xhci, "%s: OC_DET Register = %x\n",
__func__, readl(tegra->padctl_base + padregs->oc_det_0));
xhci_dbg(xhci, "%s: usb2_bchrg_otgpad0_ctl0_0 Register = %x\n",
__func__,
readl(tegra->padctl_base + padregs->usb2_bchrg_otgpad0_ctl0_0));
xhci_dbg(xhci, "%s: usb2_bchrg_otgpad1_ctl0_0 Register = %x\n",
__func__,
readl(tegra->padctl_base + padregs->usb2_bchrg_otgpad1_ctl0_0));
xhci_dbg(xhci, "%s: usb2_bchrg_bias_pad_0 Register = %x\n",
__func__,
readl(tegra->padctl_base + padregs->usb2_bchrg_bias_pad_0));
if (elpg_program0 & (SS_PORT0_WAKEUP_EVENT | SS_PORT1_WAKEUP_EVENT))
tegra->ss_wake_event = true;
else if (elpg_program0 & (USB2_PORT0_WAKEUP_EVENT |
USB2_PORT1_WAKEUP_EVENT |
USB2_PORT2_WAKEUP_EVENT |
USB2_HSIC_PORT0_WAKEUP_EVENT |
USB2_HSIC_PORT1_WAKEUP_EVENT))
tegra->hs_wake_event = true;
if (tegra->ss_wake_event || tegra->hs_wake_event) {
if (tegra->ss_pwr_gated && !tegra->host_pwr_gated) {
xhci_err(xhci, "SS gated Host ungated. Should not happen\n");
WARN_ON(tegra->ss_pwr_gated && tegra->host_pwr_gated);
} else if (tegra->ss_pwr_gated
&& tegra->host_pwr_gated) {
xhci_dbg(xhci, "[%s] schedule host_elpg_exit_work\n",
__func__);
schedule_work(&tegra->host_elpg_exit_work);
}
} else {
xhci_err(xhci, "error: wake due to no hs/ss event\n");
tegra_usb_pad_reg_write(padregs->elpg_program_0, 0xffffffff);
}
spin_unlock(&tegra->lock);
return IRQ_HANDLED;
}
static irqreturn_t tegra_xhci_smi_irq(int irq, void *ptrdev)
{
struct tegra_xhci_hcd *tegra = (struct tegra_xhci_hcd *) ptrdev;
u32 temp;
spin_lock(&tegra->lock);
/* clear the mbox intr status 1st thing. Other
* bits are W1C bits, so just write to SMI bit.
*/
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);
spin_unlock(&tegra->lock);
return IRQ_HANDLED;
}
static void tegra_xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
/*
* As of now platform drivers don't provide MSI support so we ensure
* 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_SPURIOUS_REBOOT;
}
/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
{
return xhci_gen_setup(hcd, tegra_xhci_plat_quirks);
}
static int tegra_xhci_request_mem_region(struct platform_device *pdev,
const char *name, void __iomem **region)
{
struct resource *res;
void __iomem *mem;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
if (!res) {
dev_err(&pdev->dev, "memory resource %s doesn't exist\n", name);
return -ENODEV;
}
mem = devm_request_and_ioremap(&pdev->dev, res);
if (!mem) {
dev_err(&pdev->dev, "failed to ioremap for %s\n", name);
return -EFAULT;
}
*region = mem;
return 0;
}
static int tegra_xhci_request_irq(struct platform_device *pdev,
const char *rscname, 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);
if (!res) {
dev_err(&pdev->dev, "irq resource %s doesn't exist\n", rscname);
return -ENODEV;
}
ret = devm_request_irq(&pdev->dev, res->start, handler, irqflags,
devname, tegra);
if (ret != 0) {
dev_err(&pdev->dev,
"failed to request_irq for %s (irq %d), error = %d\n",
devname, res->start, ret);
return ret;
}
*irq_no = res->start;
return 0;
}
#ifdef CONFIG_PM
static int tegra_xhci_bus_suspend(struct usb_hcd *hcd)
{
struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int err = 0;
unsigned long flags;
mutex_lock(&tegra->sync_lock);
if (xhci->shared_hcd == hcd) {
tegra->usb3_rh_suspend = true;
xhci_dbg(xhci, "%s: usb3 root hub\n", __func__);
} else if (xhci->main_hcd == hcd) {
tegra->usb2_rh_suspend = true;
xhci_dbg(xhci, "%s: usb2 root hub\n", __func__);
}
WARN_ON(tegra->hc_in_elpg);
/* suspend xhci bus. This will also set remote mask */
err = xhci_bus_suspend(hcd);
if (err) {
xhci_err(xhci, "%s: xhci_bus_suspend failed %d\n",
__func__, err);
goto xhci_bus_suspend_failed;
}
if (!(tegra->usb2_rh_suspend && tegra->usb3_rh_suspend))
goto done; /* one of the root hubs is still working */
spin_lock_irqsave(&tegra->lock, flags);
tegra->hc_in_elpg = true;
spin_unlock_irqrestore(&tegra->lock, flags);
WARN_ON(tegra->ss_pwr_gated && tegra->host_pwr_gated);
/* save xhci spec ctx. Already done by xhci_suspend */
err = xhci_suspend(tegra->xhci);
if (err) {
xhci_err(xhci, "%s: xhci_suspend failed %d\n", __func__, err);
goto xhci_suspend_failed;
}
/* Powergate host. Include ss power gate if not already done */
err = tegra_xhci_host_elpg_entry(tegra);
if (err) {
xhci_err(xhci, "%s: unable to perform elpg entry %d\n",
__func__, err);
goto tegra_xhci_host_elpg_entry_failed;
}
/* At this point,ensure ss/hs intr enables are always on */
tegra_xhci_ss_wake_on_interrupts(tegra->bdata->portmap, true);
tegra_xhci_hs_wake_on_interrupts(tegra->bdata->portmap, true);
/* In ELPG, firmware log context is gone. Rewind shared log buffer. */
if (fw_log_wait_empty_timeout(tegra, 100))
xhci_warn(xhci, "%s still has logs\n", __func__);
tegra->log.dequeue = tegra->log.virt_addr;
tegra->log.seq = 0;
done:
/* pads are disabled only if usb2 root hub in xusb is idle */
/* pads will actually be disabled only when all usb2 ports are idle */
if (xhci->main_hcd == hcd) {
utmi_phy_pad_disable();
utmi_phy_iddq_override(true);
}
mutex_unlock(&tegra->sync_lock);
return 0;
tegra_xhci_host_elpg_entry_failed:
xhci_suspend_failed:
tegra->hc_in_elpg = false;
xhci_bus_suspend_failed:
if (xhci->shared_hcd == hcd)
tegra->usb3_rh_suspend = false;
else if (xhci->main_hcd == hcd)
tegra->usb2_rh_suspend = false;
mutex_unlock(&tegra->sync_lock);
return err;
}
/* First, USB2HCD and then USB3HCD resume will be called */
static int tegra_xhci_bus_resume(struct usb_hcd *hcd)
{
struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int err = 0;
mutex_lock(&tegra->sync_lock);
tegra->host_resume_req = true;
if (xhci->shared_hcd == hcd)
xhci_dbg(xhci, "%s: usb3 root hub\n", __func__);
else if (xhci->main_hcd == hcd)
xhci_dbg(xhci, "%s: usb2 root hub\n", __func__);
/* pads are disabled only if usb2 root hub in xusb is idle */
/* pads will actually be disabled only when all usb2 ports are idle */
if (xhci->main_hcd == hcd && tegra->usb2_rh_suspend) {
utmi_phy_pad_enable();
utmi_phy_iddq_override(false);
}
if (tegra->usb2_rh_suspend && tegra->usb3_rh_suspend) {
if (tegra->ss_pwr_gated && tegra->host_pwr_gated)
tegra_xhci_host_partition_elpg_exit(tegra);
}
/* handle remote wakeup before resuming bus */
wait_remote_wakeup_ports(hcd);
err = xhci_bus_resume(hcd);
if (err) {
xhci_err(xhci, "%s: xhci_bus_resume failed %d\n",
__func__, err);
goto xhci_bus_resume_failed;
}
if (xhci->shared_hcd == hcd)
tegra->usb3_rh_suspend = false;
else if (xhci->main_hcd == hcd)
tegra->usb2_rh_suspend = false;
mutex_unlock(&tegra->sync_lock);
return 0;
xhci_bus_resume_failed:
/* TODO: reverse elpg? */
mutex_unlock(&tegra->sync_lock);
return err;
}
#endif
static irqreturn_t tegra_xhci_irq(struct usb_hcd *hcd)
{
struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
irqreturn_t iret = IRQ_HANDLED;
u32 status;
spin_lock(&tegra->lock);
if (tegra->hc_in_elpg) {
spin_lock(&xhci->lock);
if (HCD_HW_ACCESSIBLE(hcd)) {
status = xhci_readl(xhci, &xhci->op_regs->status);
status |= STS_EINT;
xhci_writel(xhci, status, &xhci->op_regs->status);
}
xhci_dbg(xhci, "%s: schedule host_elpg_exit_work\n",
__func__);
schedule_work(&tegra->host_elpg_exit_work);
spin_unlock(&xhci->lock);
} else
iret = xhci_irq(hcd);
spin_unlock(&tegra->lock);
wake_up_interruptible(&tegra->log.intr_wait);
return iret;
}
static const struct hc_driver tegra_plat_xhci_driver = {
.description = "tegra-xhci",
.product_desc = "Nvidia xHCI Host Controller",
.hcd_priv_size = sizeof(struct xhci_hcd *),
/*
* generic hardware linkage
*/
.irq = tegra_xhci_irq,
.flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
/*
* basic lifecycle operations
*/
.reset = xhci_plat_setup,
.start = xhci_run,
.stop = xhci_stop,
.shutdown = xhci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = xhci_urb_enqueue,
.urb_dequeue = xhci_urb_dequeue,
.alloc_dev = xhci_alloc_dev,
.free_dev = xhci_free_dev,
.alloc_streams = xhci_alloc_streams,
.free_streams = xhci_free_streams,
.add_endpoint = xhci_add_endpoint,
.drop_endpoint = xhci_drop_endpoint,
.endpoint_reset = xhci_endpoint_reset,
.check_bandwidth = xhci_check_bandwidth,
.reset_bandwidth = xhci_reset_bandwidth,
.address_device = xhci_address_device,
.update_hub_device = xhci_update_hub_device,
.reset_device = xhci_discover_or_reset_device,
/*
* scheduling support
*/
.get_frame_number = xhci_get_frame,
/* Root hub support */
.hub_control = xhci_hub_control,
.hub_status_data = xhci_hub_status_data,
#ifdef CONFIG_PM
.bus_suspend = tegra_xhci_bus_suspend,
.bus_resume = tegra_xhci_bus_resume,
#endif
};
#ifdef CONFIG_PM
static int
tegra_xhci_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = tegra->xhci;
int ret = 0;
mutex_lock(&tegra->sync_lock);
if (!tegra->hc_in_elpg) {
xhci_warn(xhci, "%s: lp0 suspend entry while elpg not done\n",
__func__);
mutex_unlock(&tegra->sync_lock);
return -EBUSY;
}
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) {
xhci_err(xhci,
"%s: Couldn't enable USB host mode wakeup, irq=%d, error=%d\n",
__func__, tegra->padctl_irq, ret);
}
/* enable_irq_wake for utmip/uhisc wakes */
ret = enable_irq_wake(tegra->usb3_irq);
if (ret < 0) {
xhci_err(xhci,
"%s: Couldn't enable utmip/uhsic wakeup, irq=%d, error=%d\n",
__func__, tegra->usb3_irq, ret);
}
/* enable_irq_wake for utmip/uhisc wakes */
ret = enable_irq_wake(tegra->usb2_irq);
if (ret < 0) {
xhci_err(xhci,
"%s: Couldn't enable utmip/uhsic wakeup, irq=%d, error=%d\n",
__func__, tegra->usb2_irq, ret);
}
regulator_disable(tegra->xusb_s1p8v_reg);
regulator_disable(tegra->xusb_s1p05v_reg);
tegra_usb2_clocks_deinit(tegra);
return ret;
}
static int
tegra_xhci_resume(struct platform_device *pdev)
{
struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
dev_dbg(&pdev->dev, "%s\n", __func__);
tegra->last_jiffies = jiffies;
disable_irq_wake(tegra->padctl_irq);
disable_irq_wake(tegra->usb3_irq);
disable_irq_wake(tegra->usb2_irq);
tegra->lp0_exit = true;
regulator_enable(tegra->xusb_s1p05v_reg);
regulator_enable(tegra->xusb_s1p8v_reg);
tegra_usb2_clocks_init(tegra);
return 0;
}
#endif
static int init_bootloader_firmware(struct tegra_xhci_hcd *tegra)
{
struct platform_device *pdev = tegra->pdev;
void __iomem *fw_mmio_base;
phys_addr_t fw_mem_phy_addr;
size_t fw_size;
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));
if (!fw_mmio_base) {
dev_err(&pdev->dev, "error mapping fw memory 0x%x\n",
fw_mem_phy_addr);
return -ENOMEM;
}
fw_size = ioread32(fw_mmio_base + FW_SIZE_OFFSET);
devm_iounmap(&pdev->dev, fw_mmio_base);
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;
}
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);
/* all set and ready to go */
tegra->firmware.data = fw_mmio_base;
tegra->firmware.dma = fw_dma;
tegra->firmware.size = fw_size;
return 0;
#ifdef CONFIG_PLATFORM_ENABLE_IOMMU
error_iounmap:
devm_iounmap(&pdev->dev, fw_mmio_base);
return ret;
#endif
}
static void deinit_bootloader_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);
memset(&tegra->firmware, 0, sizeof(tegra->firmware));
}
static int init_firmware(struct tegra_xhci_hcd *tegra)
{
return init_bootloader_firmware(tegra);
}
static void deinit_firmware(struct tegra_xhci_hcd *tegra)
{
deinit_bootloader_firmware(tegra);
}
static int tegra_enable_xusb_clk(struct tegra_xhci_hcd *tegra,
struct platform_device *pdev)
{
int err = 0;
/* enable ss clock */
err = clk_enable(tegra->host_clk);
if (err) {
dev_err(&pdev->dev, "Failed to enable host partition clk\n");
goto enable_host_clk_failed;
}
err = clk_enable(tegra->ss_clk);
if (err) {
dev_err(&pdev->dev, "Failed to enable ss partition clk\n");
goto eanble_ss_clk_failed;
}
err = clk_enable(tegra->emc_clk);
if (err) {
dev_err(&pdev->dev, "Failed to enable xusb.emc clk\n");
goto eanble_emc_clk_failed;
}
return 0;
eanble_emc_clk_failed:
clk_disable(tegra->ss_clk);
eanble_ss_clk_failed:
clk_disable(tegra->host_clk);
enable_host_clk_failed:
if (tegra->pdata->quirks & TEGRA_XUSB_USE_HS_SRC_CLOCK2)
clk_disable(tegra->pll_re_vco_clk);
return err;
}
static struct tegra_xusb_padctl_regs t114_padregs_offset = {
.boot_media_0 = 0x0,
.usb2_pad_mux_0 = 0x4,
.usb2_port_cap_0 = 0x8,
.snps_oc_map_0 = 0xc,
.usb2_oc_map_0 = 0x10,
.ss_port_map_0 = 0x14,
.oc_det_0 = 0x18,
.elpg_program_0 = 0x1c,
.usb2_bchrg_otgpad0_ctl0_0 = 0x20,
.usb2_bchrg_otgpad0_ctl1_0 = 0xffff,
.usb2_bchrg_otgpad1_ctl0_0 = 0x24,
.usb2_bchrg_otgpad1_ctl1_0 = 0xffff,
.usb2_bchrg_otgpad2_ctl0_0 = 0xffff,
.usb2_bchrg_otgpad2_ctl1_0 = 0xffff,
.usb2_bchrg_bias_pad_0 = 0x28,
.usb2_bchrg_tdcd_dbnc_timer_0 = 0x2c,
.iophy_pll_p0_ctl1_0 = 0x30,
.iophy_pll_p0_ctl2_0 = 0x34,
.iophy_pll_p0_ctl3_0 = 0x38,
.iophy_pll_p0_ctl4_0 = 0x3c,
.iophy_usb3_pad0_ctl1_0 = 0x40,
.iophy_usb3_pad1_ctl1_0 = 0x44,
.iophy_usb3_pad0_ctl2_0 = 0x48,
.iophy_usb3_pad1_ctl2_0 = 0x4c,
.iophy_usb3_pad0_ctl3_0 = 0x50,
.iophy_usb3_pad1_ctl3_0 = 0x54,
.iophy_usb3_pad0_ctl4_0 = 0x58,
.iophy_usb3_pad1_ctl4_0 = 0x5c,
.iophy_misc_pad_p0_ctl1_0 = 0x60,
.iophy_misc_pad_p1_ctl1_0 = 0x64,
.iophy_misc_pad_p0_ctl2_0 = 0x68,
.iophy_misc_pad_p1_ctl2_0 = 0x6c,
.iophy_misc_pad_p0_ctl3_0 = 0x70,
.iophy_misc_pad_p1_ctl3_0 = 0x74,
.iophy_misc_pad_p0_ctl4_0 = 0x78,
.iophy_misc_pad_p1_ctl4_0 = 0x7c,
.iophy_misc_pad_p0_ctl5_0 = 0x80,
.iophy_misc_pad_p1_ctl5_0 = 0x84,
.iophy_misc_pad_p0_ctl6_0 = 0x88,
.iophy_misc_pad_p1_ctl6_0 = 0x8c,
.usb2_otg_pad0_ctl0_0 = 0x90,
.usb2_otg_pad1_ctl0_0 = 0x94,
.usb2_otg_pad2_ctl0_0 = 0xffff,
.usb2_otg_pad0_ctl1_0 = 0x98,
.usb2_otg_pad1_ctl1_0 = 0x9c,
.usb2_otg_pad2_ctl1_0 = 0xffff,
.usb2_bias_pad_ctl0_0 = 0xa0,
.usb2_bias_pad_ctl1_0 = 0xa4,
.usb2_hsic_pad0_ctl0_0 = 0xa8,
.usb2_hsic_pad1_ctl0_0 = 0xac,
.usb2_hsic_pad0_ctl1_0 = 0xb0,
.usb2_hsic_pad1_ctl1_0 = 0xb4,
.usb2_hsic_pad0_ctl2_0 = 0xb8,
.usb2_hsic_pad1_ctl2_0 = 0xbc,
.ulpi_link_trim_ctl0 = 0xc0,
.ulpi_null_clk_trim_ctl0 = 0xc4,
.hsic_strb_trim_ctl0 = 0xc8,
.wake_ctl0 = 0xcc,
.pm_spare0 = 0xd0,
.iophy_misc_pad_p2_ctl1_0 = 0xffff,
.iophy_misc_pad_p3_ctl1_0 = 0xffff,
.iophy_misc_pad_p4_ctl1_0 = 0xffff,
.iophy_misc_pad_p2_ctl2_0 = 0xffff,
.iophy_misc_pad_p3_ctl2_0 = 0xffff,
.iophy_misc_pad_p4_ctl2_0 = 0xffff,
.iophy_misc_pad_p2_ctl3_0 = 0xffff,
.iophy_misc_pad_p3_ctl3_0 = 0xffff,
.iophy_misc_pad_p4_ctl3_0 = 0xffff,
.iophy_misc_pad_p2_ctl4_0 = 0xffff,
.iophy_misc_pad_p3_ctl4_0 = 0xffff,
.iophy_misc_pad_p4_ctl4_0 = 0xffff,
.iophy_misc_pad_p2_ctl5_0 = 0xffff,
.iophy_misc_pad_p3_ctl5_0 = 0xffff,
.iophy_misc_pad_p4_ctl5_0 = 0xffff,
.iophy_misc_pad_p2_ctl6_0 = 0xffff,
.iophy_misc_pad_p3_ctl6_0 = 0xffff,
.iophy_misc_pad_p4_ctl6_0 = 0xffff,
.usb3_pad_mux_0 = 0xffff,
.iophy_pll_s0_ctl1_0 = 0xffff,
.iophy_pll_s0_ctl2_0 = 0xffff,
.iophy_pll_s0_ctl3_0 = 0xffff,
.iophy_pll_s0_ctl4_0 = 0xffff,
.iophy_misc_pad_s0_ctl1_0 = 0xffff,
.iophy_misc_pad_s0_ctl2_0 = 0xffff,
.iophy_misc_pad_s0_ctl3_0 = 0xffff,
.iophy_misc_pad_s0_ctl4_0 = 0xffff,
.iophy_misc_pad_s0_ctl5_0 = 0xffff,
.iophy_misc_pad_s0_ctl6_0 = 0xffff,
};
static struct tegra_xusb_padctl_regs t124_padregs_offset = {
.boot_media_0 = 0x0,
.usb2_pad_mux_0 = 0x4,
.usb2_port_cap_0 = 0x8,
.snps_oc_map_0 = 0xc,
.usb2_oc_map_0 = 0x10,
.ss_port_map_0 = 0x14,
.oc_det_0 = 0x18,
.elpg_program_0 = 0x1c,
.usb2_bchrg_otgpad0_ctl0_0 = 0x20,
.usb2_bchrg_otgpad0_ctl1_0 = 0x24,
.usb2_bchrg_otgpad1_ctl0_0 = 0x28,
.usb2_bchrg_otgpad1_ctl1_0 = 0x2c,
.usb2_bchrg_otgpad2_ctl0_0 = 0x30,
.usb2_bchrg_otgpad2_ctl1_0 = 0x34,
.usb2_bchrg_bias_pad_0 = 0x38,
.usb2_bchrg_tdcd_dbnc_timer_0 = 0x3c,
.iophy_pll_p0_ctl1_0 = 0x40,
.iophy_pll_p0_ctl2_0 = 0x44,
.iophy_pll_p0_ctl3_0 = 0x48,
.iophy_pll_p0_ctl4_0 = 0x4c,
.iophy_usb3_pad0_ctl1_0 = 0x50,
.iophy_usb3_pad1_ctl1_0 = 0x54,
.iophy_usb3_pad0_ctl2_0 = 0x58,
.iophy_usb3_pad1_ctl2_0 = 0x5c,
.iophy_usb3_pad0_ctl3_0 = 0x60,
.iophy_usb3_pad1_ctl3_0 = 0x64,
.iophy_usb3_pad0_ctl4_0 = 0x68,
.iophy_usb3_pad1_ctl4_0 = 0x6c,
.iophy_misc_pad_p0_ctl1_0 = 0x70,
.iophy_misc_pad_p1_ctl1_0 = 0x74,
.iophy_misc_pad_p0_ctl2_0 = 0x78,
.iophy_misc_pad_p1_ctl2_0 = 0x7c,
.iophy_misc_pad_p0_ctl3_0 = 0x80,
.iophy_misc_pad_p1_ctl3_0 = 0x84,
.iophy_misc_pad_p0_ctl4_0 = 0x88,
.iophy_misc_pad_p1_ctl4_0 = 0x8c,
.iophy_misc_pad_p0_ctl5_0 = 0x90,
.iophy_misc_pad_p1_ctl5_0 = 0x94,
.iophy_misc_pad_p0_ctl6_0 = 0x98,
.iophy_misc_pad_p1_ctl6_0 = 0x9c,
.usb2_otg_pad0_ctl0_0 = 0xa0,
.usb2_otg_pad1_ctl0_0 = 0xa4,
.usb2_otg_pad2_ctl0_0 = 0xa8,
.usb2_otg_pad0_ctl1_0 = 0xac,
.usb2_otg_pad1_ctl1_0 = 0xb0,
.usb2_otg_pad2_ctl1_0 = 0xb4,
.usb2_bias_pad_ctl0_0 = 0xb8,
.usb2_bias_pad_ctl1_0 = 0xbc,
.usb2_hsic_pad0_ctl0_0 = 0xc0,
.usb2_hsic_pad1_ctl0_0 = 0xc4,
.usb2_hsic_pad0_ctl1_0 = 0xc8,
.usb2_hsic_pad1_ctl1_0 = 0xcc,
.usb2_hsic_pad0_ctl2_0 = 0xd0,
.usb2_hsic_pad1_ctl2_0 = 0xd4,
.ulpi_link_trim_ctl0 = 0xd8,
.ulpi_null_clk_trim_ctl0 = 0xdc,
.hsic_strb_trim_ctl0 = 0xe0,
.wake_ctl0 = 0xe4,
.pm_spare0 = 0xe8,
.iophy_misc_pad_p2_ctl1_0 = 0xec,
.iophy_misc_pad_p3_ctl1_0 = 0xf0,
.iophy_misc_pad_p4_ctl1_0 = 0xf4,
.iophy_misc_pad_p2_ctl2_0 = 0xf8,
.iophy_misc_pad_p3_ctl2_0 = 0xfc,
.iophy_misc_pad_p4_ctl2_0 = 0x100,
.iophy_misc_pad_p2_ctl3_0 = 0x104,
.iophy_misc_pad_p3_ctl3_0 = 0x108,
.iophy_misc_pad_p4_ctl3_0 = 0x10c,
.iophy_misc_pad_p2_ctl4_0 = 0x110,
.iophy_misc_pad_p3_ctl4_0 = 0x114,
.iophy_misc_pad_p4_ctl4_0 = 0x118,
.iophy_misc_pad_p2_ctl5_0 = 0x11c,
.iophy_misc_pad_p3_ctl5_0 = 0x120,
.iophy_misc_pad_p4_ctl5_0 = 0x124,
.iophy_misc_pad_p2_ctl6_0 = 0x128,
.iophy_misc_pad_p3_ctl6_0 = 0x12c,
.iophy_misc_pad_p4_ctl6_0 = 0x130,
.usb3_pad_mux_0 = 0x134,
.iophy_pll_s0_ctl1_0 = 0x138,
.iophy_pll_s0_ctl2_0 = 0x13c,
.iophy_pll_s0_ctl3_0 = 0x140,
.iophy_pll_s0_ctl4_0 = 0x144,
.iophy_misc_pad_s0_ctl1_0 = 0x148,
.iophy_misc_pad_s0_ctl2_0 = 0x14c,
.iophy_misc_pad_s0_ctl3_0 = 0x150,
.iophy_misc_pad_s0_ctl4_0 = 0x154,
.iophy_misc_pad_s0_ctl5_0 = 0x158,
.iophy_misc_pad_s0_ctl6_0 = 0x15c,
};
/* FIXME: using notifier to transfer control to host from suspend
* for otg port when xhci is in elpg. Find better alternative
*/
static int tegra_xhci_otg_notify(struct notifier_block *nb,
unsigned long event, void *unused)
{
struct tegra_xhci_hcd *tegra = container_of(nb,
struct tegra_xhci_hcd, otgnb);
if ((event == USB_EVENT_ID))
if (tegra->hc_in_elpg) {
schedule_work(&tegra->host_elpg_exit_work);
tegra->host_resume_req = true;
}
return NOTIFY_OK;
}
/* 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;
BUILD_BUG_ON(sizeof(struct cfgtbl) != 256);
if (usb_disabled())
return -ENODEV;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra) {
dev_err(&pdev->dev, "memory alloc failed\n");
return -ENOMEM;
}
tegra->pdev = pdev;
tegra->pdata = dev_get_platdata(&pdev->dev);
tegra->bdata = tegra->pdata->bdata;
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);
if (ret) {
dev_err(&pdev->dev, "failed to map padctl\n");
return ret;
}
ret = tegra_xhci_request_mem_region(pdev, "fpci", &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);
if (ret) {
dev_err(&pdev->dev, "failed to map ipfs\n");
return ret;
}
ret = tegra_xusb_partitions_clk_init(tegra);
if (ret) {
dev_err(&pdev->dev,
"failed to initialize xusb partitions clocks\n");
return ret;
}
if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P0) {
tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
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");
goto err_deinit_xusb_partition_clk;
}
/* Enable UTMIP, PLLU and PLLE */
ret = tegra_usb2_clocks_init(tegra);
if (ret) {
dev_err(&pdev->dev, "error initializing usb2 clocks\n");
goto err_deinit_tegra_xusb_regulator;
}
/* tegra_unpowergate_partition also does partition reset deassert */
ret = tegra_unpowergate_partition(TEGRA_POWERGATE_XUSBA);
if (ret)
dev_err(&pdev->dev, "could not unpowergate xusba partition\n");
/* tegra_unpowergate_partition also does partition reset deassert */
ret = tegra_unpowergate_partition(TEGRA_POWERGATE_XUSBC);
if (ret)
dev_err(&pdev->dev, "could not unpowergate xusbc partition\n");
ret = tegra_enable_xusb_clk(tegra, pdev);
if (ret)
dev_err(&pdev->dev, "could not enable partition clock\n");
/* reset the pointer back to NULL. driver uses it */
/* platform_set_drvdata(pdev, NULL); */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "host");
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_virt_base = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (!tegra->host_phy_virt_base) {
dev_err(&pdev->dev, "error mapping host phy memory\n");
ret = -ENOMEM;
goto err_deinit_usb2_clocks;
}
/* Setup IPFS access and BAR0 space */
tegra_xhci_cfg(tegra);
val = readl(tegra->fpci_base + XUSB_CFG_0);
tegra->device_id = (val >> 16) & 0xffff;
dev_info(&pdev->dev, "XUSB device id = 0x%x (%s)\n", tegra->device_id,
(XUSB_DEVICE_ID_T114 == tegra->device_id) ? "T114" : "T124+");
if (XUSB_DEVICE_ID_T114 == tegra->device_id) {
tegra->padregs = &t114_padregs_offset;
} else if (XUSB_DEVICE_ID_T124 == tegra->device_id) {
tegra->padregs = &t124_padregs_offset;
} else {
dev_info(&pdev->dev, "XUSB device_id neither T114 nor T124!\n");
dev_info(&pdev->dev, "XUSB using T124 pad register offsets!\n");
tegra->padregs = &t124_padregs_offset;
}
/* calculate rctrl_val and tctrl_val once at boot time */
/* Issue is only applicable for T114 */
if (XUSB_DEVICE_ID_T114 == tegra->device_id)
tegra_xhci_war_for_tctrl_rctrl(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);
/* Release XUSB wake logic state latching */
tegra_xhci_ss_wake_signal(tegra->bdata->portmap, false);
tegra_xhci_ss_vcore(tegra->bdata->portmap, false);
/* Deassert reset to XUSB host, ss, dev clocks */
tegra_periph_reset_deassert(tegra->host_clk);
tegra_periph_reset_deassert(tegra->ss_clk);
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;
}
ret = load_firmware(tegra, true /* do reset ARU */);
if (ret < 0) {
dev_err(&pdev->dev, "failed to load firmware\n");
ret = -ENODEV;
goto err_deinit_firmware;
}
spin_lock_init(&tegra->lock);
mutex_init(&tegra->sync_lock);
mutex_init(&tegra->mbox_lock);
pmc_init(tegra);
device_init_wakeup(&pdev->dev, 1);
driver = &tegra_plat_xhci_driver;
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;
}
ret = tegra_xhci_request_mem_region(pdev, "host", &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);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
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) {
dev_err(&pdev->dev, "failed to add usb2hcd, error = %d\n", ret);
goto err_put_usb2_hcd;
}
/* USB 2.0 roothub is stored in the platform_device now. */
hcd = dev_get_drvdata(&pdev->dev);
xhci = hcd_to_xhci(hcd);
tegra->xhci = xhci;
platform_set_drvdata(pdev, tegra);
xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
dev_name(&pdev->dev), hcd);
if (!xhci->shared_hcd) {
dev_err(&pdev->dev, "failed to create usb3 hcd\n");
ret = -ENOMEM;
goto err_remove_usb2_hcd;
}
/*
* Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
* is called by usb_add_hcd().
*/
*((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret) {
dev_err(&pdev->dev, "failed to add usb3hcd, error = %d\n", ret);
goto err_put_usb3_hcd;
}
device_init_wakeup(&hcd->self.root_hub->dev, 1);
device_init_wakeup(&xhci->shared_hcd->self.root_hub->dev, 1);
/* do mailbox related initializations */
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);
/* do host partition elpg exit related initialization */
INIT_WORK(&tegra->host_elpg_exit_work, host_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,
IRQF_SHARED, "tegra_xhci_mbox_irq", &tegra->smi_irq);
if (ret != 0)
goto err_remove_usb3_hcd;
/* Register interrupt handler for PADCTRL line to
* handle wake on connect irqs interrupt from
* firmware
*/
ret = tegra_xhci_request_irq(pdev, "padctl", 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,
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,
IRQF_SHARED | IRQF_TRIGGER_HIGH, "pmc_usb_phy_wake_isr",
&tegra->usb3_irq);
if (ret != 0)
goto err_remove_usb3_hcd;
for (port = 0; port < XUSB_SS_PORT_COUNT; port++) {
tegra->ctle_ctx_saved[port] = false;
tegra->dfe_ctx_saved[port] = false;
}
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);
return 0;
err_remove_usb3_hcd:
usb_remove_hcd(xhci->shared_hcd);
err_put_usb3_hcd:
usb_put_hcd(xhci->shared_hcd);
err_remove_usb2_hcd:
kfree(tegra->xhci);
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);
for_each_enabled_hsic_pad(pad, tegra) {
hsic_pad_disable(tegra, pad);
hsic_power_rail_disable(tegra);
}
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);
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);
return 0;
}
static void tegra_xhci_shutdown(struct platform_device *pdev)
{
struct tegra_xhci_hcd *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = NULL;
struct usb_hcd *hcd = NULL;
if (tegra == NULL)
return;
if (tegra->hc_in_elpg) {
pmc_disable_bus_ctrl(tegra);
} else {
xhci = tegra->xhci;
hcd = xhci_to_hcd(xhci);
xhci_shutdown(hcd);
}
}
static struct platform_driver tegra_xhci_driver = {
.probe = tegra_xhci_probe,
.remove = tegra_xhci_remove,
.shutdown = tegra_xhci_shutdown,
#ifdef CONFIG_PM
.suspend = tegra_xhci_suspend,
.resume = tegra_xhci_resume,
#endif
.driver = {
.name = "tegra-xhci",
},
};
MODULE_ALIAS("platform:tegra-xhci");
int tegra_xhci_register_plat(void)
{
return platform_driver_register(&tegra_xhci_driver);
}
void tegra_xhci_unregister_plat(void)
{
platform_driver_unregister(&tegra_xhci_driver);
}