kernel_samsung_a53x/drivers/usb/host/xhci-exynos.c

2206 lines
59 KiB
C
Raw Normal View History

2024-06-15 21:02:09 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* xhci-plat.c - xHCI host controller driver platform Bus Glue.
*
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
* Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* A lot of code borrowed from the Linux xHCI driver.
*/
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/usb/phy.h>
#include <linux/slab.h>
#include <linux/phy/phy.h>
#include <linux/acpi.h>
#include <linux/usb/of.h>
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
#include "../../../sound/usb/exynos_usb_audio.h"
#include <linux/types.h>
#include "xhci-trace.h"
#endif
#include "../core/hub.h"
#include "../core/phy.h"
#include "xhci.h"
#include "xhci-plat.h"
#include "xhci-mvebu.h"
#include "xhci-rcar.h"
#include "../dwc3/dwc3-exynos.h"
#include "../dwc3/exynos-otg.h"
#include "xhci-exynos.h"
#include <soc/samsung/exynos-cpupm.h>
static struct hc_driver __read_mostly xhci_exynos_hc_driver;
static int xhci_exynos_setup(struct usb_hcd *hcd);
static int xhci_exynos_start(struct usb_hcd *hcd);
static int xhci_exynos_bus_suspend(struct usb_hcd *hcd);
static int xhci_exynos_bus_resume(struct usb_hcd *hcd);
static int xhci_exynos_wake_lock(struct xhci_hcd_exynos *xhci_exynos,
int is_main_hcd, int is_lock);
static int is_rewa_enabled;
static struct xhci_hcd_exynos *g_xhci_exynos;
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
extern struct usb_xhci_pre_alloc xhci_pre_alloc;
extern struct hcd_hw_info *g_hwinfo;
extern int xhci_check_trb_in_td_math(struct xhci_hcd *xhci);
void xhci_exynos_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
struct xhci_ring *xhci_ring_alloc_uram(struct xhci_hcd *xhci,
unsigned int num_segs, unsigned int cycle_state,
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags,
u32 endpoint_type);
u32 ext_ep_type = 0;
static void *dma_pre_alloc_coherent(struct xhci_hcd *xhci, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
struct usb_xhci_pre_alloc *xhci_alloc = g_xhci_exynos->xhci_alloc;
u64 align = size % PAGE_SIZE;
u64 b_offset = xhci_alloc->offset;
if (align)
xhci_alloc->offset = xhci_alloc->offset + size + (PAGE_SIZE - align);
else
xhci_alloc->offset = xhci_alloc->offset + size;
*dma_handle = xhci_alloc->dma + b_offset;
return (void *)xhci_alloc->pre_dma_alloc + b_offset;
}
static void xhci_exynos_usb_offload_enable_event_ring(struct xhci_hcd *xhci)
{
u32 temp;
u64 temp_64;
temp_64 = xhci_read_64(xhci, &g_xhci_exynos->ir_set_audio->erst_dequeue);
temp_64 &= ~ERST_PTR_MASK;
xhci_info(xhci, "ERST2 deq = 64'h%0lx", (unsigned long) temp_64);
xhci_info(xhci, "// [USB Audio] Set the interrupt modulation register");
temp = readl(&g_xhci_exynos->ir_set_audio->irq_control);
temp &= ~ER_IRQ_INTERVAL_MASK;
/*
* the increment interval is 8 times as much as that defined
* in xHCI spec on MTK's controller
*/
temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
writel(temp, &g_xhci_exynos->ir_set_audio->irq_control);
temp = readl(&g_xhci_exynos->ir_set_audio->irq_pending);
xhci_info(xhci, "// [USB Audio] Enabling event ring interrupter %p by writing 0x%x to irq_pending",
g_xhci_exynos->ir_set_audio, (unsigned int) ER_IRQ_ENABLE(temp));
writel(ER_IRQ_ENABLE(temp), &g_xhci_exynos->ir_set_audio->irq_pending);
}
static void xhci_exynos_usb_offload_store_hw_info(struct xhci_hcd *xhci, struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_virt_device *virt_dev;
struct xhci_erst_entry *entry = &g_xhci_exynos->erst_audio.entries[0];
virt_dev = xhci->devs[udev->slot_id];
g_hwinfo->dcbaa_dma = xhci->dcbaa->dma;
g_hwinfo->save_dma = g_xhci_exynos->save_dma;
g_hwinfo->cmd_ring = xhci->op_regs->cmd_ring;
g_hwinfo->slot_id = udev->slot_id;
g_hwinfo->in_dma = g_xhci_exynos->in_dma;
g_hwinfo->in_buf = g_xhci_exynos->in_addr;
g_hwinfo->out_dma = g_xhci_exynos->out_dma;
g_hwinfo->out_buf = g_xhci_exynos->out_addr;
g_hwinfo->in_ctx = virt_dev->in_ctx->dma;
g_hwinfo->out_ctx = virt_dev->out_ctx->dma;
g_hwinfo->erst_addr = entry->seg_addr;
g_hwinfo->speed = udev->speed;
if (xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO)
g_hwinfo->use_uram = true;
else
g_hwinfo->use_uram = false;
pr_info("<<< %s\n", __func__);
}
static void xhci_exynos_set_hc_event_deq_audio(struct xhci_hcd *xhci)
{
u64 temp;
dma_addr_t deq;
deq = xhci_trb_virt_to_dma(g_xhci_exynos->event_ring_audio->deq_seg,
g_xhci_exynos->event_ring_audio->dequeue);
if (deq == 0 && !in_interrupt())
xhci_warn(xhci, "WARN something wrong with SW event ring "
"dequeue ptr.\n");
/* Update HC event ring dequeue pointer */
temp = xhci_read_64(xhci, &g_xhci_exynos->ir_set_audio->erst_dequeue);
temp &= ERST_PTR_MASK;
/* Don't clear the EHB bit (which is RW1C) because
* there might be more events to service.
*/
temp &= ~ERST_EHB;
xhci_info(xhci, "//[%s] Write event ring dequeue pointer = 0x%llx, "
"preserving EHB bit",__func__,
((u64) deq & (u64) ~ERST_PTR_MASK) | temp);
xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
&g_xhci_exynos->ir_set_audio->erst_dequeue);
}
void xhci_exynos_parse_endpoint(struct xhci_hcd *xhci, struct usb_device *udev, struct usb_endpoint_descriptor *desc, struct xhci_container_ctx *ctx)
{
struct usb_endpoint_descriptor *d = desc;
int size = 0x100;
unsigned ep_index;
g_hwinfo->rawdesc_length = size;
ep_index = xhci_get_endpoint_index(d);
pr_info("udev = 0x%8x, Ep = 0x%x, desc = 0x%8x\n", udev, d->bEndpointAddress, d);
if (( d->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) ==
USB_ENDPOINT_XFER_ISOC) {
if ((d->bmAttributes & USB_ENDPOINT_USAGE_MASK) ==
USB_ENDPOINT_USAGE_FEEDBACK) {
/* Only Feedback endpoint(Not implict feedback data endpoint) */
if (d->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
g_hwinfo->fb_in_ep =
d->bEndpointAddress;
pr_info("Feedback IN ISO endpoint #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
} else {
g_hwinfo->fb_out_ep =
d->bEndpointAddress;
pr_info("Feedback OUT ISO endpoint #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
}
} else {
/* Data Stream Endpoint only */
if (d->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
if (d->bEndpointAddress != g_hwinfo->fb_in_ep) {
g_hwinfo->in_ep =
d->bEndpointAddress;
pr_info(" This is IN ISO endpoint #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
} else {
pr_info("IN ISO endpoint is same with FB #0%x\n",
d->bEndpointAddress);
}
if ((d->bLength > 7) && (d->bSynchAddress != 0x0)) {
g_hwinfo->fb_out_ep =
d->bSynchAddress;
pr_info("Feedback OUT ISO endpoint #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
}
} else {
g_hwinfo->out_ep =
d->bEndpointAddress;
pr_info(" This is OUT ISO endpoint #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
if ((d->bLength > 7) && (d->bSynchAddress != 0x0)) {
g_hwinfo->fb_in_ep =
d->bSynchAddress;
pr_info("Feedback IN ISO endpoint #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
}
}
}
}
}
int xhci_exynos_address_device(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci;
int ret;
pr_debug("%s +++", __func__);
ret = xhci_address_device(hcd, udev);
xhci = hcd_to_xhci(hcd);
xhci_exynos_usb_offload_store_hw_info(xhci, hcd, udev);
pr_debug("%s ---", __func__);
return ret;
}
int xhci_exynos_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
int ret;
struct xhci_hcd *xhci;
struct xhci_virt_device *virt_dev;
struct usb_endpoint_descriptor *d = &ep->desc;
/* Check Feedback Endpoint */
if ((d->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_ISOC) {
if ((d->bmAttributes & USB_ENDPOINT_USAGE_MASK) ==
USB_ENDPOINT_USAGE_FEEDBACK) {
/* Only Feedback endpoint(Not implict feedback data endpoint) */
if (d->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
g_hwinfo->fb_in_ep =
d->bEndpointAddress;
pr_info("Feedback IN EP used #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
} else {
g_hwinfo->fb_out_ep =
d->bEndpointAddress;
pr_info("Feedback OUT EP used #0%x 0x%x\n",
d->bEndpointAddress, d->bSynchAddress);
}
}
}
/* Check Feedback EP is already allocated */
if (g_hwinfo->fb_in_ep == d->bEndpointAddress ||
g_hwinfo->fb_out_ep == d->bEndpointAddress)
g_hwinfo->feedback = 1;
else
g_hwinfo->feedback = 0;
pr_debug("%s +++", __func__);
ret = xhci_add_endpoint(hcd, udev, ep);
g_hwinfo->feedback = 0;
if (!ret && udev->slot_id) {
xhci = hcd_to_xhci(hcd);
virt_dev = xhci->devs[udev->slot_id];
xhci_exynos_parse_endpoint(xhci, udev, &ep->desc, virt_dev->out_ctx);
}
pr_debug("%s ---", __func__);
return ret;
}
static int xhci_exynos_alloc_event_ring(struct xhci_hcd *xhci, gfp_t flags)
{
dma_addr_t dma;
unsigned int val;
u64 val_64;
struct xhci_segment *seg;
g_xhci_exynos->ir_set_audio = &xhci->run_regs->ir_set[1];
g_xhci_exynos->save_addr = dma_pre_alloc_coherent(xhci,
sizeof(PAGE_SIZE), &dma, flags);
g_xhci_exynos->save_dma = dma;
xhci_info(xhci, "// Save address = 0x%llx (DMA), %p (virt)",
(unsigned long long)g_xhci_exynos->save_dma, g_xhci_exynos->save_addr);
if ((xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO)) {
/* for AUDIO erst */
g_xhci_exynos->event_ring_audio = xhci_ring_alloc_uram(xhci, ERST_NUM_SEGS,
1, TYPE_EVENT, 0, flags, 0);
if (!g_xhci_exynos->event_ring_audio)
goto fail;
g_xhci_exynos->erst_audio.entries = ioremap(EXYNOS_URAM_ABOX_ERST_SEG_ADDR,
sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);
if (!g_xhci_exynos->erst_audio.entries)
goto fail;
dma = EXYNOS_URAM_ABOX_ERST_SEG_ADDR;
xhci_info(xhci, "ABOX audio ERST allocated at 0x%x",
EXYNOS_URAM_ABOX_ERST_SEG_ADDR);
} else {
/* for AUDIO erst */
g_xhci_exynos->event_ring_audio = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1,
TYPE_EVENT, 0, flags);
if (!g_xhci_exynos->event_ring_audio)
goto fail;
if (xhci_check_trb_in_td_math(xhci) < 0)
goto fail;
g_xhci_exynos->erst_audio.entries = dma_pre_alloc_coherent(xhci,
sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
flags);
if (!g_xhci_exynos->erst_audio.entries)
goto fail;
}
xhci_info(xhci, "// Allocated event ring segment table at 0x%llx",
(unsigned long long)dma);
memset(g_xhci_exynos->erst_audio.entries, 0, sizeof(struct xhci_erst_entry) *
ERST_NUM_SEGS);
g_xhci_exynos->erst_audio.num_entries = ERST_NUM_SEGS;
g_xhci_exynos->erst_audio.erst_dma_addr = dma;
xhci_info(xhci, "// Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx",
xhci->erst.num_entries,
xhci->erst.entries,
(unsigned long long)xhci->erst.erst_dma_addr);
/* set ring base address and size for each segment table entry */
for (val = 0, seg = g_xhci_exynos->event_ring_audio->first_seg;
val < ERST_NUM_SEGS; val++) {
struct xhci_erst_entry *entry = &g_xhci_exynos->erst_audio.entries[val];
entry->seg_addr = cpu_to_le64(seg->dma);
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
entry->rsvd = 0;
seg = seg->next;
}
/* set ERST count with the number of entries in the segment table */
val = readl(&g_xhci_exynos->ir_set_audio->erst_size);
val &= ERST_SIZE_MASK;
val |= ERST_NUM_SEGS;
xhci_info(xhci, "// Write ERST size = %i to ir_set 0 (some bits preserved)",
val);
writel(val, &g_xhci_exynos->ir_set_audio->erst_size);
xhci_info(xhci, "// Set ERST entries to point to event ring.");
/* set the segment table base address */
xhci_info(xhci, "// Set ERST base address for ir_set 0 = 0x%llx",
(unsigned long long)g_xhci_exynos->erst_audio.erst_dma_addr);
val_64 = xhci_read_64(xhci, &g_xhci_exynos->ir_set_audio->erst_base);
val_64 &= ERST_PTR_MASK;
val_64 |= (g_xhci_exynos->erst_audio.erst_dma_addr & (u64) ~ERST_PTR_MASK);
xhci_write_64(xhci, val_64, &g_xhci_exynos->ir_set_audio->erst_base);
/* Set the event ring dequeue address */
xhci_exynos_set_hc_event_deq_audio(xhci);
xhci_info(xhci, "// Wrote ERST address to ir_set 1.");
return 0;
fail:
return -1;
}
static void xhci_exynos_free_event_ring(struct xhci_hcd *xhci)
{
if (xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO)
iounmap(g_xhci_exynos->erst_audio.entries);
else
g_xhci_exynos->erst_audio.entries = NULL;
xhci_info(xhci, "%s: Freed ERST for Audio offloading", __func__);
if (g_xhci_exynos->event_ring_audio)
xhci_exynos_ring_free(xhci, g_xhci_exynos->event_ring_audio);
g_xhci_exynos->event_ring_audio = NULL;
xhci_info(xhci, "%s: Freed event ring for Audio offloading", __func__);
}
static void xhci_exynos_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx)
{
/* Ignore dma_pool_free if it is allocated from URAM */
if (ctx->dma != EXYNOS_URAM_DEVICE_CTX_ADDR)
dma_pool_free(xhci->device_pool, ctx->bytes, ctx->dma);
}
static void xhci_exynos_alloc_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, int type, gfp_t flags)
{
if (type != XHCI_CTX_TYPE_INPUT && g_xhci_exynos->exynos_uram_ctx_alloc == 0 &&
xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO) {
/* Only first Device Context uses URAM */
int i;
ctx->bytes = ioremap(EXYNOS_URAM_DEVICE_CTX_ADDR, 2112);
if (!ctx->bytes)
return;
for (i = 0; i < 2112; i++)
ctx->bytes[i] = 0;
ctx->dma = EXYNOS_URAM_DEVICE_CTX_ADDR;
g_xhci_exynos->usb_audio_ctx_addr = ctx->bytes;
g_xhci_exynos->exynos_uram_ctx_alloc = 1;
xhci_info(xhci, "First device context allocated at URAM(%x)",
EXYNOS_URAM_DEVICE_CTX_ADDR);
} else {
ctx->bytes = dma_pool_zalloc(xhci->device_pool, flags, &ctx->dma);
if (!ctx->bytes)
return;
}
}
static struct xhci_ring *xhci_exynos_alloc_transfer_ring(struct xhci_hcd *xhci,
u32 endpoint_type, enum xhci_ring_type ring_type, unsigned int max_packet, gfp_t mem_flags)
{
if (xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO) {
/* If URAM is not allocated, it try to allocate from URAM */
if (g_xhci_exynos->exynos_uram_isoc_out_alloc == 0 &&
endpoint_type == ISOC_OUT_EP) {
xhci_info(xhci, "First ISOC OUT ring is allocated from URAM.\n");
return xhci_ring_alloc_uram(xhci, 1, 1, ring_type,
max_packet, mem_flags,
endpoint_type);
g_xhci_exynos->exynos_uram_isoc_out_alloc = 1;
} else if (g_xhci_exynos->exynos_uram_isoc_in_alloc == 0 &&
endpoint_type == ISOC_IN_EP &&
EXYNOS_URAM_ISOC_IN_RING_ADDR != 0x0) {
xhci_info(xhci, "First ISOC IN ring is allocated from URAM.\n");
return xhci_ring_alloc_uram(xhci, 1, 1, ring_type,
max_packet, mem_flags,
endpoint_type);
g_xhci_exynos->exynos_uram_isoc_in_alloc = 1;
} else {
return xhci_ring_alloc(xhci, 2, 1, ring_type,
max_packet, mem_flags);
}
} else {
return xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
}
}
void xhci_exynos_segment_free_skip(struct xhci_hcd *xhci, struct xhci_segment *seg)
{
if (seg->trbs) {
/* Check URAM address for memory free */
if (seg->dma == EXYNOS_URAM_ABOX_EVT_RING_ADDR) {
iounmap(seg->trbs);
} else if (seg->dma == EXYNOS_URAM_ISOC_OUT_RING_ADDR) {
g_xhci_exynos->exynos_uram_isoc_out_alloc = 0;
if (in_interrupt())
g_xhci_exynos->usb_audio_isoc_out_addr = (u8 *)seg->trbs;
else
iounmap(seg->trbs);
} else if (seg->dma == EXYNOS_URAM_ISOC_IN_RING_ADDR) {
g_xhci_exynos->exynos_uram_isoc_in_alloc = 0;
if (in_interrupt())
g_xhci_exynos->usb_audio_isoc_in_addr = (u8 *)seg->trbs;
else
iounmap(seg->trbs);
} else
dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
seg->trbs = NULL;
}
kfree(seg->bounce_buf);
kfree(seg);
}
void xhci_exynos_free_segments_for_ring(struct xhci_hcd *xhci,
struct xhci_segment *first)
{
struct xhci_segment *seg;
seg = first->next;
if (!seg)
xhci_err(xhci, "segment is null unexpectedly\n");
while (seg != first) {
struct xhci_segment *next = seg->next;
xhci_exynos_segment_free_skip(xhci, seg);
seg = next;
}
xhci_exynos_segment_free_skip(xhci, first);
}
static void xhci_exynos_remove_segment_mapping(struct radix_tree_root *trb_address_map,
struct xhci_segment *seg)
{
unsigned long key;
key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
if (radix_tree_lookup(trb_address_map, key))
radix_tree_delete(trb_address_map, key);
}
static void xhci_exynos_remove_stream_mapping(struct xhci_ring *ring)
{
struct xhci_segment *seg;
if (WARN_ON_ONCE(ring->trb_address_map == NULL))
return;
seg = ring->first_seg;
do {
xhci_exynos_remove_segment_mapping(ring->trb_address_map, seg);
seg = seg->next;
} while (seg != ring->first_seg);
}
void xhci_exynos_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
if (!ring)
return;
//trace_xhci_ring_free(ring);
if (ring->first_seg) {
if (ring->type == TYPE_STREAM)
xhci_exynos_remove_stream_mapping(ring);
xhci_exynos_free_segments_for_ring(xhci, ring->first_seg);
}
kfree(ring);
}
int endpoint_ring_table[31];
static void xhci_exynos_free_transfer_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev, unsigned int ep_index)
{
if (endpoint_ring_table[ep_index]) {
pr_info("%s: endpoint %d has been already free-ing\n",
__func__, ep_index);
}
if (xhci->quirks & BIT_ULL(60)) {
pr_info("%s: endpoint %d free is requested from reset_bandwidth\n",
__func__, ep_index);
xhci_exynos_ring_free(xhci, virt_dev->eps[ep_index].new_ring);
xhci->quirks &= ~(BIT_ULL(60));
return;
}
endpoint_ring_table[ep_index] = 1;
xhci_exynos_ring_free(xhci, virt_dev->eps[ep_index].ring);
endpoint_ring_table[ep_index] = 0;
return;
}
static int xhci_exynos_vendor_init(struct xhci_hcd *xhci)
{
return 0;
}
static void xhci_exynos_vendor_cleanup(struct xhci_hcd *xhci)
{
xhci_exynos_free_event_ring(xhci);
return;
}
static bool xhci_exynos_is_usb_offload_enabled(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev, unsigned int ep_index)
{
return true;
}
static struct xhci_device_context_array *xhci_exynos_alloc_dcbaa(
struct xhci_hcd *xhci, gfp_t flags)
{
dma_addr_t dma;
if (xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO) {
int i;
xhci_info(xhci, "DCBAA is allocated at 0x%x(URAM)",
EXYNOS_URAM_DCBAA_ADDR);
/* URAM allocation for DCBAA */
xhci->dcbaa = ioremap(EXYNOS_URAM_DCBAA_ADDR,
sizeof(*xhci->dcbaa));
if (!xhci->dcbaa)
return NULL;
/* Clear DCBAA */
for (i = 0; i < MAX_HC_SLOTS; i++)
xhci->dcbaa->dev_context_ptrs[i] = 0x0;
xhci->dcbaa->dma = EXYNOS_URAM_DCBAA_ADDR;
} else {
xhci->dcbaa = dma_pre_alloc_coherent(xhci, sizeof(*xhci->dcbaa),
&dma, flags);
if (!xhci->dcbaa)
return NULL;
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
xhci->dcbaa->dma = dma;
}
return xhci->dcbaa;
}
static void xhci_exynos_free_dcbaa(struct xhci_hcd *xhci)
{
if (xhci->quirks & XHCI_USE_URAM_FOR_EXYNOS_AUDIO) {
iounmap(xhci->dcbaa);
if (g_xhci_exynos->usb_audio_ctx_addr != NULL) {
iounmap(g_xhci_exynos->usb_audio_ctx_addr);
g_xhci_exynos->usb_audio_ctx_addr = NULL;
}
if (g_xhci_exynos->usb_audio_isoc_out_addr != NULL) {
iounmap(g_xhci_exynos->usb_audio_isoc_out_addr);
g_xhci_exynos->usb_audio_isoc_out_addr = NULL;
}
if (g_xhci_exynos->usb_audio_isoc_in_addr != NULL) {
iounmap(g_xhci_exynos->usb_audio_isoc_in_addr);
g_xhci_exynos->usb_audio_isoc_in_addr = NULL;
}
} else
xhci->dcbaa = NULL;
}
/* URAM Allocation Functions */
extern void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg);
extern void xhci_link_segments(struct xhci_segment *prev,
struct xhci_segment *next,
enum xhci_ring_type type, bool chain_links);
extern void xhci_initialize_ring_info(struct xhci_ring *ring,
unsigned int cycle_state);
static struct xhci_segment *xhci_segment_alloc_uram(struct xhci_hcd *xhci,
unsigned int cycle_state,
unsigned int max_packet,
gfp_t flags)
{
struct xhci_segment *seg;
dma_addr_t dma;
int i;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
seg = kzalloc_node(sizeof(*seg), flags, dev_to_node(dev));
if (!seg)
return NULL;
seg->trbs = ioremap(EXYNOS_URAM_ABOX_EVT_RING_ADDR, TRB_SEGMENT_SIZE);
if (!seg->trbs)
return NULL;
dma = EXYNOS_URAM_ABOX_EVT_RING_ADDR;
if (max_packet) {
seg->bounce_buf = kzalloc_node(max_packet, flags,
dev_to_node(dev));
if (!seg->bounce_buf) {
dma_pool_free(xhci->segment_pool, seg->trbs, dma);
kfree(seg);
return NULL;
}
}
/* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
if (cycle_state == 0) {
for (i = 0; i < TRBS_PER_SEGMENT; i++)
seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE);
}
seg->dma = dma;
xhci_info(xhci, "ABOX Event Ring is allocated at 0x%x",
EXYNOS_URAM_ABOX_EVT_RING_ADDR);
seg->next = NULL;
return seg;
}
static struct xhci_segment *xhci_segment_alloc_uram_ep(struct xhci_hcd *xhci,
unsigned int cycle_state,
unsigned int max_packet,
gfp_t flags, int seg_num,
u32 endpoint_type)
{
struct xhci_segment *seg;
dma_addr_t dma;
int i;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
seg = kzalloc_node(sizeof(*seg), flags, dev_to_node(dev));
if (!seg)
return NULL;
if (seg_num != 0) {
/* Support just one segment */
xhci_err(xhci, "%s : Unexpected SEG NUMBER!\n", __func__);
return NULL;
}
if (endpoint_type == ISOC_OUT_EP) {
if (!g_hwinfo->feedback)
seg->trbs = ioremap(EXYNOS_URAM_ISOC_OUT_RING_ADDR,
TRB_SEGMENT_SIZE);
else
seg->trbs = dma_pool_zalloc(xhci->segment_pool, flags, &dma);
if (!seg->trbs)
return NULL;
if (!g_hwinfo->feedback)
dma = EXYNOS_URAM_ISOC_OUT_RING_ADDR;
xhci_info(xhci, "First ISOC-OUT Ring is allocated at 0x%8x", dma);
} else if (endpoint_type == ISOC_IN_EP) {
if (!g_hwinfo->feedback)
seg->trbs = ioremap(EXYNOS_URAM_ISOC_IN_RING_ADDR,
TRB_SEGMENT_SIZE);
else
seg->trbs = dma_pool_zalloc(xhci->segment_pool, flags, &dma);
if (!seg->trbs)
return NULL;
if (!g_hwinfo->feedback)
dma = EXYNOS_URAM_ISOC_IN_RING_ADDR;
xhci_info(xhci, "First ISOC-IN Ring is allocated at 0x%8x", dma);
} else {
xhci_err(xhci, "%s : Unexpected EP Type!\n", __func__);
return NULL;
}
for (i = 0; i < 256; i++) {
seg->trbs[i].link.segment_ptr = 0;
seg->trbs[i].link.intr_target = 0;
seg->trbs[i].link.control = 0;
}
if (max_packet) {
seg->bounce_buf = kzalloc_node(max_packet, flags,
dev_to_node(dev));
if (!seg->bounce_buf) {
dma_pool_free(xhci->segment_pool, seg->trbs, dma);
kfree(seg);
return NULL;
}
}
/* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
if (cycle_state == 0) {
for (i = 0; i < TRBS_PER_SEGMENT; i++)
seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE);
}
seg->dma = dma;
seg->next = NULL;
return seg;
}
static int xhci_alloc_segments_for_ring_uram(struct xhci_hcd *xhci,
struct xhci_segment **first, struct xhci_segment **last,
unsigned int num_segs, unsigned int cycle_state,
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags,
u32 endpoint_type)
{
struct xhci_segment *prev;
bool chain_links;
/* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
chain_links = !!(xhci_link_trb_quirk(xhci) ||
(type == TYPE_ISOC &&
(xhci->quirks & XHCI_AMD_0x96_HOST)));
if (type == TYPE_ISOC) {
prev = xhci_segment_alloc_uram_ep(xhci, cycle_state,
max_packet, flags, 0,
endpoint_type);
} else if (type == TYPE_EVENT) {
prev = xhci_segment_alloc_uram(xhci, cycle_state, max_packet, flags);
} else {
xhci_err(xhci, "Unexpected TYPE for URAM allocation!\n");
return -ENOMEM;
}
if (!prev)
return -ENOMEM;
num_segs--;
*first = prev;
while (num_segs > 0) {
struct xhci_segment *next = NULL;
if (type == TYPE_ISOC) {
prev = xhci_segment_alloc_uram_ep(xhci, cycle_state,
max_packet, flags, 1,
endpoint_type);
} else if (type == TYPE_EVENT) {
next = xhci_segment_alloc_uram(xhci, cycle_state,
max_packet, flags);
} else {
xhci_err(xhci, "Unexpected TYPE for URAM alloc(multi)!\n");
return -ENOMEM;
}
if (!next) {
prev = *first;
while (prev) {
next = prev->next;
xhci_segment_free(xhci, prev);
prev = next;
}
return -ENOMEM;
}
xhci_link_segments(prev, next, type, chain_links);
prev = next;
num_segs--;
}
xhci_link_segments(prev, *first, type, chain_links);
*last = prev;
return 0;
}
struct xhci_ring *xhci_ring_alloc_uram(struct xhci_hcd *xhci,
unsigned int num_segs, unsigned int cycle_state,
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags,
u32 endpoint_type)
{
struct xhci_ring *ring;
int ret;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
ring = kzalloc_node(sizeof(*ring), flags, dev_to_node(dev));
if (!ring)
return NULL;
ring->num_segs = num_segs;
ring->bounce_buf_len = max_packet;
INIT_LIST_HEAD(&ring->td_list);
ring->type = type;
if (num_segs == 0)
return ring;
ret = xhci_alloc_segments_for_ring_uram(xhci, &ring->first_seg,
&ring->last_seg, num_segs, cycle_state, type,
max_packet, flags, endpoint_type);
if (ret)
goto fail;
/* Only event ring does not use link TRB */
if (type != TYPE_EVENT) {
/* See section 4.9.2.1 and 6.4.4.1 */
ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |=
cpu_to_le32(LINK_TOGGLE);
}
xhci_initialize_ring_info(ring, cycle_state);
//trace_xhci_ring_alloc(ring);
return ring;
fail:
kfree(ring);
return NULL;
}
//#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
static unsigned int xhci_exynos_get_endpoint_address(unsigned int ep_index)
{
unsigned int number = DIV_ROUND_UP(ep_index, 2);
unsigned int direction = ep_index % 2 ? USB_DIR_OUT : USB_DIR_IN;
return direction | number;
}
int xhci_exynos_sync_dev_ctx(struct xhci_hcd *xhci, unsigned int slot_id)
{
struct xhci_virt_device *virt_dev;
struct xhci_slot_ctx *slot_ctx;
int i;
int last_ep;
int last_ep_ctx = 31;
/* Use default hwinfo in case
* there are no audio devices occupied
*/
virt_dev = xhci->devs[slot_id];
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
last_ep = LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info));
if (last_ep < 31)
last_ep_ctx = last_ep + 1;
for (i = 0; i < last_ep_ctx; ++i) {
unsigned int epaddr = xhci_exynos_get_endpoint_address(i);
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, i);
if (epaddr == g_hwinfo->in_ep) {
pr_info("[%s] ep%d set in deq : %#08llx\n",
__func__, i, ep_ctx->deq);
g_hwinfo->old_in_deq = g_hwinfo->in_deq;
g_hwinfo->in_deq = ep_ctx->deq;
} else if (epaddr == g_hwinfo->out_ep) {
pr_info("[%s] ep%d set out deq : %#08llx\n",
__func__, i, ep_ctx->deq);
g_hwinfo->old_out_deq = g_hwinfo->out_deq;
g_hwinfo->out_deq = ep_ctx->deq;
} else if (epaddr == g_hwinfo->fb_out_ep) {
pr_info("[%s] ep%d set fb out deq : %#08llx\n",
__func__, i, ep_ctx->deq);
g_hwinfo->fb_old_out_deq = g_hwinfo->fb_out_deq;
g_hwinfo->fb_out_deq = ep_ctx->deq;
} else if (epaddr == g_hwinfo->fb_in_ep) {
pr_info("[%s] ep%d set fb in deq : %#08llx\n",
__func__, i, ep_ctx->deq);
g_hwinfo->fb_old_in_deq = g_hwinfo->fb_in_deq;
g_hwinfo->fb_in_deq = ep_ctx->deq;
}
}
return 0;
}
#endif
extern struct usb_xhci_pre_alloc xhci_pre_alloc;
static const struct xhci_driver_overrides xhci_exynos_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_exynos_priv),
.reset = xhci_exynos_setup,
.start = xhci_exynos_start,
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
.add_endpoint = xhci_exynos_add_endpoint,
.address_device = xhci_exynos_address_device,
#endif
.bus_suspend = xhci_exynos_bus_suspend,
.bus_resume = xhci_exynos_bus_resume,
};
int xhci_exynos_bus_suspend(struct usb_hcd *hcd)
{
struct xhci_exynos_priv *priv = hcd_to_xhci_exynos_priv(hcd);
struct xhci_hcd_exynos *xhci_exynos = priv->xhci_exynos;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret, main_hcd;
pr_info("%s, priv = 0x%8x, xhci_exynos = 0x%8x\n",
__func__, priv, xhci_exynos);
if (hcd == xhci->main_hcd)
main_hcd = 1;
else
main_hcd = 0;
ret = xhci_bus_suspend(hcd);
#if 0
if (hcd == xhci->main_hcd &&
xhci_exynos->port_state == PORT_USB2) {
xhci_info(xhci, "port is connected, phy vendor set\n");
ret = exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb2, 1, 0);
if (ret) {
xhci_info(xhci, "phy vendor set fail\n");
//return ret;
}
}
#endif
xhci_exynos_wake_lock(xhci_exynos, main_hcd, 0);
return ret;
}
int xhci_exynos_bus_resume(struct usb_hcd *hcd)
{
struct xhci_exynos_priv *priv = hcd_to_xhci_exynos_priv(hcd);
struct xhci_hcd_exynos *xhci_exynos = priv->xhci_exynos;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret, main_hcd;
if (hcd == xhci->main_hcd)
main_hcd = 1;
else
main_hcd = 0;
pr_info("%s, hcd = 0x%8x\n", __func__, hcd);
#if 0
if (hcd == xhci->main_hcd &&
xhci_exynos->port_state == PORT_USB2) {
xhci_info(xhci, "[%s] phy vendor set\n", __func__);
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb2, 1, 1);
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb2, 0, 0);
}
#endif
ret = xhci_bus_resume(hcd);
xhci_exynos_wake_lock(xhci_exynos, main_hcd, 1);
return ret;
}
static void xhci_priv_exynos_start(struct usb_hcd *hcd)
{
struct xhci_exynos_priv *priv = hcd_to_xhci_exynos_priv(hcd);
if (priv->plat_start)
priv->plat_start(hcd);
}
void xhci_exynos_portsc_power_off(void __iomem *portsc, u32 on, u32 prt)
{
struct xhci_hcd_exynos *xhci_exynos = g_xhci_exynos;
u32 reg;
spin_lock(&xhci_exynos->xhcioff_lock);
pr_info("%s, on=%d portsc_control_priority=%d, prt=%d\n",
__func__, on, xhci_exynos->portsc_control_priority, prt);
if (xhci_exynos->portsc_control_priority > prt) {
spin_unlock(&xhci_exynos->xhcioff_lock);
return;
}
xhci_exynos->portsc_control_priority = prt;
if (on && !xhci_exynos->port_off_done) {
pr_info("%s, Do not switch-on port\n", __func__);
spin_unlock(&xhci_exynos->xhcioff_lock);
return;
}
reg = readl(portsc);
if (on)
reg |= PORT_POWER;
else
reg &= ~PORT_POWER;
writel(reg, portsc);
reg = readl(portsc);
pr_info("power %s portsc, reg = 0x%x addr = %8x\n",
on ? "on" : "off", reg, (u64)portsc);
#if 0
reg = readl(phycon_base_addr + 0x70);
if (on)
reg &= ~DIS_RX_DETECT;
else
reg |= DIS_RX_DETECT;
writel(reg, phycon_base_addr + 0x70);
pr_info("phycon ess_ctrl = 0x%x\n", readl(phycon_base_addr + 0x70));
#endif
if (on)
xhci_exynos->port_off_done = 0;
else
xhci_exynos->port_off_done = 1;
spin_unlock(&xhci_exynos->xhcioff_lock);
}
int xhci_exynos_portsc_set(u32 on)
{
if (g_xhci_exynos->usb3_portsc != NULL && !on) {
xhci_exynos_portsc_power_off(g_xhci_exynos->usb3_portsc, 0, 2);
g_xhci_exynos->port_set_delayed = 0;
return 0;
}
if (!on)
g_xhci_exynos->port_set_delayed = 1;
pr_info("%s, usb3_portsc is NULL\n", __func__);
return -EIO;
}
int xhci_exynos_port_power_set(u32 on, u32 prt)
{
if (g_xhci_exynos->usb3_portsc) {
xhci_exynos_portsc_power_off(g_xhci_exynos->usb3_portsc, on, prt);
return 0;
}
pr_info("%s, usb3_portsc is NULL\n", __func__);
return -EIO;
}
EXPORT_SYMBOL_GPL(xhci_exynos_port_power_set);
static int xhci_exynos_check_port(struct usb_device *dev, bool on)
{
struct usb_device *hdev;
struct usb_device *udev = dev;
struct device *ddev = &udev->dev;
struct xhci_hcd_exynos *xhci_exynos = g_xhci_exynos;
enum usb_port_state pre_state;
int usb3_hub_detect = 0;
int usb2_detect = 0;
int port;
int bInterfaceClass = 0;
if (udev->bus->root_hub == udev) {
pr_info("this dev is a root hub\n");
goto skip;
}
pre_state = xhci_exynos->port_state;
/* Find root hub */
hdev = udev->parent;
if (!hdev)
goto skip;
hdev = dev->bus->root_hub;
if (!hdev)
goto skip;
pr_info("root hub maxchild = %d\n", hdev->maxchild);
/* check all ports */
usb_hub_for_each_child(hdev, port, udev) {
dev_dbg(ddev, "%s, class = %d, speed = %d\n",
__func__, udev->descriptor.bDeviceClass,
udev->speed);
dev_dbg(ddev, "udev = 0x%8x, state = %d\n", udev, udev->state);
if (udev && udev->state == USB_STATE_CONFIGURED) {
if (!dev->config->interface[0])
continue;
bInterfaceClass = udev->config->interface[0]
->cur_altsetting->desc.bInterfaceClass;
if (on) {
#if defined(CONFIG_USB_HOST_SAMSUNG_FEATURE)
if (bInterfaceClass == USB_CLASS_AUDIO) {
#else
if (bInterfaceClass == USB_CLASS_HID ||
bInterfaceClass == USB_CLASS_AUDIO) {
#endif
udev->do_remote_wakeup =
(udev->config->desc.bmAttributes &
USB_CONFIG_ATT_WAKEUP) ? 1 : 0;
if (udev->do_remote_wakeup == 1) {
device_init_wakeup(ddev, 1);
usb_enable_autosuspend(dev);
}
dev_dbg(ddev, "%s, remote_wakeup = %d\n",
__func__, udev->do_remote_wakeup);
}
}
if (bInterfaceClass == USB_CLASS_HUB) {
xhci_exynos->port_state = PORT_HUB;
usb3_hub_detect = 1;
break;
} else if (bInterfaceClass == USB_CLASS_BILLBOARD) {
xhci_exynos->port_state = PORT_DP;
usb3_hub_detect = 1;
break;
}
if (udev->speed >= USB_SPEED_SUPER) {
xhci_exynos->port_state = PORT_USB3;
usb3_hub_detect = 1;
break;
} else {
xhci_exynos->port_state = PORT_USB2;
usb2_detect = 1;
}
} else {
pr_info("not configured, state = %d\n", udev->state);
}
}
if (!usb3_hub_detect && !usb2_detect)
xhci_exynos->port_state = PORT_EMPTY;
pr_info("%s %s state pre=%d now=%d\n", __func__,
on ? "on" : "off", pre_state, xhci_exynos->port_state);
return xhci_exynos->port_state;
skip:
return -EINVAL;
}
static void xhci_exynos_set_port(struct usb_device *dev, bool on)
{
//cancel_delayed_work_sync(&g_dwc->usb_qos_lock_delayed_work);
switch (xhci_exynos_check_port(dev, on)) {
case PORT_EMPTY:
pr_info("Port check empty\n");
is_otg_only = 1;
usb_power_notify_control(0, 1);
g_xhci_exynos->usb3_phy_control = true;
xhci_exynos_port_power_set(1, 1);
//dwc3_otg_qos_lock(g_dwc, 1);
break;
case PORT_USB2:
pr_info("Port check usb2\n");
is_otg_only = 0;
xhci_exynos_port_power_set(0, 1);
usb_power_notify_control(0, 0);
g_xhci_exynos->usb3_phy_control = false;
//schedule_delayed_work(&g_dwc->usb_qos_lock_delayed_work,
// msecs_to_jiffies(USB_BUS_CLOCK_DELAY_MS));
break;
case PORT_USB3:
/* xhci_port_power_set(1, 1); */
is_otg_only = 0;
pr_info("Port check usb3\n");
break;
case PORT_HUB:
/*xhci_exynos_port_power_set(1, 1);*/
pr_info("Port check hub\n");
is_otg_only = 0;
break;
case PORT_DP:
/*xhci_exynos_port_power_set(1, 1);*/
pr_info("Port check DP\n");
is_otg_only = 0;
break;
default:
break;
}
}
int xhci_exynos_inform_dp_use(int use, int lane_cnt)
{
int ret = 0;
pr_info("[%s] dp use = %d, lane_cnt = %d\n",
__func__, use, lane_cnt);
if ((use == 1) && (lane_cnt == 4)) {
#ifdef CONFIG_EXYNOS_USBDRD_PHY30
exynos_usbdrd_dp_use_notice(lane_cnt);
if (g_xhci_exynos->usb3_phy_control == 0)
usb_power_notify_control(1, 1);
#endif
ret = xhci_exynos_portsc_set(0);
udelay(1);
}
return ret;
}
EXPORT_SYMBOL(xhci_exynos_inform_dp_use);
static int xhci_exynos_power_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
xhci_exynos_set_port(dev, 1);
break;
case USB_DEVICE_REMOVE:
xhci_exynos_set_port(dev, 0);
break;
}
return NOTIFY_OK;
}
static struct notifier_block dev_nb = {
.notifier_call = xhci_exynos_power_notify,
};
void xhci_exynos_register_notify(void)
{
is_otg_only = 1;
g_xhci_exynos->port_state = PORT_EMPTY;
usb_register_notify(&dev_nb);
}
void xhci_exynos_unregister_notify(void)
{
usb_unregister_notify(&dev_nb);
}
static int xhci_priv_init_quirk(struct usb_hcd *hcd)
{
struct xhci_exynos_priv *priv = hcd_to_xhci_exynos_priv(hcd);
if (!priv->init_quirk)
return 0;
return priv->init_quirk(hcd);
}
static int xhci_priv_suspend_quirk(struct usb_hcd *hcd)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
if (!priv->suspend_quirk)
return 0;
return priv->suspend_quirk(hcd);
}
static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
{
struct xhci_exynos_priv *priv = hcd_to_xhci_exynos_priv(hcd);
if (!priv->resume_quirk)
return 0;
return priv->resume_quirk(hcd);
}
static void xhci_exynos_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct xhci_exynos_priv *priv = xhci_to_exynos_priv(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_PLAT | priv->quirks;
}
/* called during probe() after chip reset completes */
static int xhci_exynos_setup(struct usb_hcd *hcd)
{
int ret;
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
#endif
ret = xhci_priv_init_quirk(hcd);
if (ret)
return ret;
ret = xhci_gen_setup(hcd, xhci_exynos_quirks);
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
pr_debug("%s: alloc_event_ring!\n", __func__);
xhci_exynos_alloc_event_ring(xhci, GFP_KERNEL);
#endif
#if 0
/*
* DWC3 WORKAROUND: xhci reset clears PHY CR port settings,
* so USB3.0 PHY should be tuned again.
*/
if (hcd == xhci->main_hcd) {
if (xhci_exynos->phy_usb2)
exynos_usbdrd_phy_tune(xhci_exynos->phy_usb2, OTG_STATE_A_HOST);
} else {
if (xhci_exynos->phy_usb3)
exynos_usbdrd_phy_tune(xhci_exynos->phy_usb3, OTG_STATE_A_HOST);
}
#endif
return ret;
}
static int xhci_exynos_start(struct usb_hcd *hcd)
{
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
struct xhci_hcd *xhci;
#endif
int ret;
xhci_priv_exynos_start(hcd);
ret = xhci_run(hcd);
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
xhci = hcd_to_xhci(hcd);
pr_debug("%s: enable_event_ring!\n", __func__);
xhci_exynos_usb_offload_enable_event_ring(xhci);
#endif
return ret;
}
static ssize_t
xhci_exynos_ss_compliance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
u32 reg;
void __iomem *reg_base;
reg_base = hcd->regs;
reg = readl(reg_base + PORTSC_OFFSET);
return sysfs_emit(buf, "0x%x\n", reg);
}
static ssize_t
xhci_exynos_ss_compliance_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t n)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
int value;
u32 reg;
void __iomem *reg_base;
if (kstrtoint(buf, 10, &value))
return -EINVAL;
reg_base = hcd->regs;
if (value == 1) {
/* PORTSC PLS is set to 10, LWS to 1 */
reg = readl(reg_base + PORTSC_OFFSET);
reg &= ~((0xF << 5) | (1 << 16));
reg |= (10 << 5) | (1 << 16);
writel(reg, reg_base + PORTSC_OFFSET);
pr_info("SS host compliance enabled portsc 0x%x\n", reg);
} else
pr_info("Only 1 is allowed for input value\n");
return n;
}
static DEVICE_ATTR_RW(xhci_exynos_ss_compliance);
static struct attribute *xhci_exynos_attrs[] = {
&dev_attr_xhci_exynos_ss_compliance.attr,
NULL
};
ATTRIBUTE_GROUPS(xhci_exynos);
#ifdef CONFIG_OF
static const struct xhci_plat_priv xhci_plat_marvell_armada = {
.init_quirk = xhci_mvebu_mbus_init_quirk,
};
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
.init_quirk = xhci_mvebu_a3700_init_quirk,
};
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1)
};
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3)
};
static const struct of_device_id usb_xhci_of_match[] = {
{
.compatible = "generic-xhci",
}, {
.compatible = "xhci-platform",
},
{},
};
MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
#endif
static void xhci_pm_runtime_init(struct device *dev)
{
dev->power.runtime_status = RPM_SUSPENDED;
dev->power.idle_notification = false;
dev->power.disable_depth = 1;
atomic_set(&dev->power.usage_count, 0);
dev->power.runtime_error = 0;
atomic_set(&dev->power.child_count, 0);
pm_suspend_ignore_children(dev, false);
dev->power.runtime_auto = true;
dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE;
dev->power.deferred_resume = false;
dev->power.accounting_timestamp = jiffies;
dev->power.timer_expires = 0;
init_waitqueue_head(&dev->power.wait_queue);
}
static struct xhci_plat_priv_overwrite xhci_plat_vendor_overwrite;
int xhci_exynos_register_vendor_ops(struct xhci_vendor_ops *vendor_ops)
{
if (vendor_ops == NULL)
return -EINVAL;
xhci_plat_vendor_overwrite.vendor_ops = vendor_ops;
return 0;
}
EXPORT_SYMBOL_GPL(xhci_exynos_register_vendor_ops);
static int xhci_vendor_init(struct xhci_hcd *xhci)
{
struct xhci_vendor_ops *ops = NULL;
if (xhci_plat_vendor_overwrite.vendor_ops)
ops = xhci->vendor_ops = xhci_plat_vendor_overwrite.vendor_ops;
if (ops && ops->vendor_init)
return ops->vendor_init(xhci);
return 0;
}
static void xhci_vendor_cleanup(struct xhci_hcd *xhci)
{
struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
if (ops && ops->vendor_cleanup)
ops->vendor_cleanup(xhci);
xhci->vendor_ops = NULL;
}
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
static struct xhci_vendor_ops ops = {
.vendor_init = xhci_exynos_vendor_init,
.vendor_cleanup = xhci_exynos_vendor_cleanup,
.is_usb_offload_enabled = xhci_exynos_is_usb_offload_enabled,
.alloc_dcbaa = xhci_exynos_alloc_dcbaa,
.free_dcbaa = xhci_exynos_free_dcbaa,
.alloc_transfer_ring = xhci_exynos_alloc_transfer_ring,
.free_transfer_ring = xhci_exynos_free_transfer_ring,
.alloc_container_ctx = xhci_exynos_alloc_container_ctx,
.free_container_ctx = xhci_exynos_free_container_ctx,
.sync_dev_ctx = xhci_exynos_sync_dev_ctx,
};
#endif
int xhci_exynos_wake_lock(struct xhci_hcd_exynos *xhci_exynos,
int is_main_hcd, int is_lock)
{
struct usb_hcd *hcd = xhci_exynos->hcd;
int idle_ip_index;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct wakeup_source *main_wakelock, *shared_wakelock;
main_wakelock = xhci_exynos->main_wakelock;
shared_wakelock = xhci_exynos->shared_wakelock;
dev_info(xhci_exynos->dev, "<<< %s - hcd = 0x%8x 0x%8x 0x%8x\n",
__func__, hcd, main_wakelock, shared_wakelock);
if (xhci->xhc_state & XHCI_STATE_REMOVING) {
dev_info(xhci_exynos->dev, "%s - Host removing return!\n",
__func__);
return -ESHUTDOWN;
}
if (is_lock) {
if (is_main_hcd) {
dev_info(xhci_exynos->dev, "%s: Main HCD WAKE LOCK\n", __func__);
__pm_stay_awake(main_wakelock);
} else {
dev_info(xhci_exynos->dev, "%s: Shared HCD WAKE LOCK\n", __func__);
__pm_stay_awake(shared_wakelock);
}
/* Add a routine for disable IDLEIP (IP idle) */
dev_info(xhci_exynos->dev, "IDLEIP(SICD) disable.\n");
idle_ip_index = get_idle_ip_index();
pr_info("%s, usb idle ip = %d\n", __func__, idle_ip_index);
exynos_update_ip_idle_status(idle_ip_index, 0);
} else {
if (is_main_hcd) {
dev_info(xhci_exynos->dev, "%s: Main HCD WAKE UNLOCK\n", __func__);
__pm_relax(main_wakelock);
} else {
dev_info(xhci_exynos->dev, "%s: Shared HCD WAKE UNLOCK\n", __func__);
__pm_relax(shared_wakelock);
}
if (!main_wakelock->active && !shared_wakelock->active) {
xhci_info(xhci, "Try to IDLEIP Enable!!!\n");
/* Add a routine for enable IDLEIP (IP idle) */
dev_info(xhci_exynos->dev, "IDLEIP(SICD) Enable.\n");
idle_ip_index = get_idle_ip_index();
pr_info("%s, usb idle ip = %d\n", __func__, idle_ip_index);
exynos_update_ip_idle_status(idle_ip_index, 1);
}
}
return 0;
}
static int xhci_exynos_probe(struct platform_device *pdev)
{
struct device *parent = pdev->dev.parent;
const struct hc_driver *driver;
struct device *sysdev, *tmpdev;
struct xhci_hcd *xhci;
struct xhci_hcd_exynos *xhci_exynos;
struct xhci_exynos_priv *priv;
struct resource *res;
struct usb_hcd *hcd;
struct usb_device *hdev;
struct usb_hub *hub;
struct usb_port *port_dev;
int ret;
int irq;
struct wakeup_source *main_wakelock, *shared_wakelock;
int value;
dev_info(&pdev->dev, "XHCI PLAT START\n");
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
xhci_exynos_register_vendor_ops(&ops);
pr_info("%s register ops done!\n", __func__);
pr_info("%s fix ep ring free!\n", __func__);
#endif
main_wakelock = wakeup_source_register(&pdev->dev, dev_name(&pdev->dev));
if (main_wakelock)
__pm_stay_awake(main_wakelock);
else
pr_info("%s main_wakelock wakeup_source_register fail !\n", __func__);
/* Initialization shared wakelock for SS HCD */
shared_wakelock = wakeup_source_register(&pdev->dev, dev_name(&pdev->dev));
if (shared_wakelock)
__pm_stay_awake(shared_wakelock);
else
pr_info("%s shared_wakelock wakeup_source_register fail !\n", __func__);
is_rewa_enabled = 0;
if (usb_disabled())
return -ENODEV;
driver = &xhci_exynos_hc_driver;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
/*
* sysdev must point to a device that is known to the system firmware
* or PCI hardware. We handle these three cases here:
* 1. xhci_plat comes from firmware
* 2. xhci_plat is child of a device from firmware (dwc3-plat)
* 3. xhci_plat is grandchild of a pci device (dwc3-pci)
*/
for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) {
if (is_of_node(sysdev->fwnode) ||
is_acpi_device_node(sysdev->fwnode))
break;
#ifdef CONFIG_PCI
else if (sysdev->bus == &pci_bus_type)
break;
#endif
}
if (!sysdev)
sysdev = &pdev->dev;
/* Try to set 64-bit DMA first */
if (WARN_ON(!sysdev->dma_mask))
/* Platform did not initialize dma_mask */
ret = dma_coerce_mask_and_coherent(sysdev,
DMA_BIT_MASK(64));
else
ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
/* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
if (ret) {
ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(32));
if (ret)
return ret;
}
xhci_pm_runtime_init(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
dev_name(&pdev->dev), NULL);
if (!hcd) {
ret = -ENOMEM;
goto disable_runtime;
}
hcd->skip_phy_initialization = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
goto put_hcd;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
xhci = hcd_to_xhci(hcd);
g_xhci_exynos = devm_kzalloc(&pdev->dev, sizeof(struct xhci_hcd_exynos), GFP_KERNEL);
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
g_hwinfo = devm_kzalloc(hcd->self.sysdev, sizeof(struct hcd_hw_info), GFP_KERNEL);
#endif
ret = xhci_vendor_init(xhci);
if (ret)
goto put_hcd;
if (g_xhci_exynos)
xhci_exynos = g_xhci_exynos;
else
goto put_hcd;
xhci_exynos->dev = &pdev->dev;
xhci_exynos->hcd = (struct usb_hcd *)platform_get_drvdata(pdev);
platform_set_drvdata(pdev, xhci_exynos);
spin_lock_init(&xhci_exynos->xhcioff_lock);
xhci_exynos->port_off_done = 0;
xhci_exynos->portsc_control_priority = 0;
xhci_exynos->usb3_phy_control = true;
xhci_exynos->usb3_portsc = hcd->regs + PORTSC_OFFSET;
if (xhci_exynos->port_set_delayed) {
pr_info("port power set delayed\n");
xhci_exynos_portsc_power_off(xhci_exynos->usb3_portsc, 0, 2);
xhci_exynos->port_set_delayed = 0;
}
xhci_exynos_register_notify();
/*
* Not all platforms have clks so it is not an error if the
* clock do not exist.
*/
xhci->reg_clk = devm_clk_get_optional(&pdev->dev, "reg");
if (IS_ERR(xhci->reg_clk)) {
ret = PTR_ERR(xhci->reg_clk);
goto put_hcd;
}
ret = clk_prepare_enable(xhci->reg_clk);
if (ret)
goto put_hcd;
xhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(xhci->clk)) {
ret = PTR_ERR(xhci->clk);
goto disable_reg_clk;
}
ret = clk_prepare_enable(xhci->clk);
if (ret)
goto disable_reg_clk;
priv = hcd_to_xhci_exynos_priv(hcd);
priv->xhci_exynos = xhci_exynos;
device_wakeup_enable(hcd->self.controller);
xhci->main_hcd = hcd;
xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
dev_name(&pdev->dev), hcd);
if (!xhci->shared_hcd) {
ret = -ENOMEM;
goto disable_clk;
}
xhci->shared_hcd->skip_phy_initialization = 1;
xhci_exynos->shared_hcd = xhci->shared_hcd;
/* imod_interval is the interrupt moderation value in nanoseconds. */
xhci->imod_interval = 40000;
/* Iterate over all parent nodes for finding quirks */
for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
if (device_property_read_bool(tmpdev, "usb2-lpm-disable"))
xhci->quirks |= XHCI_HW_LPM_DISABLE;
if (device_property_read_bool(tmpdev, "usb3-lpm-capable"))
xhci->quirks |= XHCI_LPM_SUPPORT;
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
xhci->quirks |= XHCI_BROKEN_PORT_PED;
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}
hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
if (IS_ERR(hcd->usb_phy)) {
ret = PTR_ERR(hcd->usb_phy);
if (ret == -EPROBE_DEFER)
goto put_usb3_hcd;
hcd->usb_phy = NULL;
} else {
ret = usb_phy_init(hcd->usb_phy);
if (ret)
goto put_usb3_hcd;
}
/* Get USB2.0 PHY for main hcd */
if (parent) {
xhci_exynos->phy_usb2 = devm_phy_get(parent, "usb2-phy");
if (IS_ERR_OR_NULL(xhci_exynos->phy_usb2)) {
xhci_exynos->phy_usb2 = NULL;
dev_err(&pdev->dev,
"%s: failed to get phy\n", __func__);
}
}
/* Get USB3.0 PHY to tune the PHY */
if (parent) {
xhci_exynos->phy_usb3 =
devm_phy_get(parent, "usb3-phy");
if (IS_ERR_OR_NULL(xhci_exynos->phy_usb3)) {
xhci_exynos->phy_usb3 = NULL;
dev_err(&pdev->dev,
"%s: failed to get phy\n", __func__);
}
}
ret = of_property_read_u32(parent->of_node, "xhci_l2_support", &value);
if (ret == 0 && value == 1)
xhci->quirks |= XHCI_L2_SUPPORT;
else {
dev_err(&pdev->dev,
"can't get xhci l2 support, error = %d\n", ret);
}
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
ret = of_property_read_u32(parent->of_node,
"xhci_use_uram_for_audio", &value);
if (ret == 0 && value == 1) {
/*
* Check URAM address. At least the following address should
* be defined.(Otherwise, URAM feature will be disabled.)
*/
if (EXYNOS_URAM_DCBAA_ADDR == 0x0 ||
EXYNOS_URAM_ABOX_ERST_SEG_ADDR == 0x0 ||
EXYNOS_URAM_ABOX_EVT_RING_ADDR == 0x0 ||
EXYNOS_URAM_DEVICE_CTX_ADDR == 0x0 ||
EXYNOS_URAM_ISOC_OUT_RING_ADDR == 0x0) {
dev_info(&pdev->dev,
"Some URAM addresses are not defiend!\n");
goto skip_uram;
}
dev_info(&pdev->dev, "Support URAM for USB audio.\n");
xhci->quirks |= XHCI_USE_URAM_FOR_EXYNOS_AUDIO;
/* Initialization Default Value */
xhci_exynos->exynos_uram_ctx_alloc = false;
xhci_exynos->exynos_uram_isoc_out_alloc = false;
xhci_exynos->exynos_uram_isoc_in_alloc = false;
xhci_exynos->usb_audio_ctx_addr = NULL;
xhci_exynos->usb_audio_isoc_out_addr = NULL;
xhci_exynos->usb_audio_isoc_in_addr = NULL;
} else {
dev_err(&pdev->dev, "URAM is not used.\n");
}
skip_uram:
xhci_exynos->xhci_alloc = &xhci_pre_alloc;
#endif
if (main_wakelock)
xhci_exynos->main_wakelock = main_wakelock;
if (shared_wakelock)
xhci_exynos->shared_wakelock = shared_wakelock;
hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
xhci->shared_hcd->tpl_support = hcd->tpl_support;
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto disable_usb_phy;
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret)
goto dealloc_usb2_hcd;
/* Set port_dev quirks for reduce port initialize time */
hdev = xhci->main_hcd->self.root_hub;
hub = usb_get_intfdata(hdev->actconfig->interface[0]);
port_dev = hub->ports[0];
port_dev->quirks |= USB_PORT_QUIRK_FAST_ENUM;
/* Set port_dev quirks for reduce port initialize time */
hdev = xhci->shared_hcd->self.root_hub;
hub = usb_get_intfdata(hdev->actconfig->interface[0]);
if (hub) {
port_dev = hub->ports[0];
port_dev->quirks |= USB_PORT_QUIRK_FAST_ENUM;
}
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
ret = of_property_read_u32(parent->of_node,
"usb_audio_offloading", &value);
if (ret == 0 && value == 1) {
ret = exynos_usb_audio_init(parent, pdev);
if (ret) {
dev_err(&pdev->dev, "USB Audio INIT fail\n");
goto dealloc_usb2_hcd;
}
dev_info(&pdev->dev, "USB Audio offloading is supported\n");
} else
dev_err(&pdev->dev, "No usb offloading, err = %d\n", ret);
xhci_exynos->out_dma = xhci_data.out_data_dma;
xhci_exynos->out_addr = xhci_data.out_data_addr;
xhci_exynos->in_dma = xhci_data.in_data_dma;
xhci_exynos->in_addr = xhci_data.in_data_addr;
#endif
device_enable_async_suspend(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
device_set_wakeup_enable(&xhci->main_hcd->self.root_hub->dev, 1);
#ifdef CONFIG_EXYNOS_USBDRD_PHY30
device_set_wakeup_enable(&xhci->shared_hcd->self.root_hub->dev, 1);
#else
device_set_wakeup_enable(&xhci->shared_hcd->self.root_hub->dev, 0);
#endif
/*
* Prevent runtime pm from being on as default, users should enable
* runtime pm using power/control in sysfs.
*/
pm_runtime_forbid(&pdev->dev);
return 0;
dealloc_usb2_hcd:
usb_remove_hcd(hcd);
disable_usb_phy:
usb_phy_shutdown(hcd->usb_phy);
put_usb3_hcd:
usb_put_hcd(xhci->shared_hcd);
disable_clk:
clk_disable_unprepare(xhci->clk);
disable_reg_clk:
clk_disable_unprepare(xhci->reg_clk);
put_hcd:
xhci_exynos_unregister_notify();
usb_put_hcd(hcd);
disable_runtime:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
static int xhci_exynos_remove(struct platform_device *dev)
{
struct xhci_hcd_exynos *xhci_exynos = platform_get_drvdata(dev);
struct usb_hcd *hcd = xhci_exynos->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
struct device *parent = dev->dev.parent;
#endif
struct clk *clk = xhci->clk;
struct clk *reg_clk = xhci->reg_clk;
struct usb_hcd *shared_hcd = xhci->shared_hcd;
struct usb_device *rhdev = hcd->self.root_hub;
struct usb_device *srhdev = shared_hcd->self.root_hub;
struct usb_device *udev;
int port, need_wait, timeout;
dev_info(&dev->dev, "XHCI PLAT REMOVE\n");
#if defined(CONFIG_USB_HOST_SAMSUNG_FEATURE)
pr_info("%s\n", __func__);
/* In order to prevent kernel panic */
if (!pm_runtime_suspended(&xhci->shared_hcd->self.root_hub->dev)) {
pr_info("%s, shared_hcd pm_runtime_forbid\n", __func__);
pm_runtime_forbid(&xhci->shared_hcd->self.root_hub->dev);
}
if (!pm_runtime_suspended(&xhci->main_hcd->self.root_hub->dev)) {
pr_info("%s, main_hcd pm_runtime_forbid\n", __func__);
pm_runtime_forbid(&xhci->main_hcd->self.root_hub->dev);
}
#endif
pm_runtime_get_sync(&dev->dev);
xhci->xhc_state |= XHCI_STATE_REMOVING;
if (xhci_exynos->port_state == PORT_USB2) {
usb_power_notify_control(0, 1);
}
xhci_exynos_port_power_set(1, 3);
xhci_exynos->usb3_portsc = NULL;
xhci_exynos->port_set_delayed = 0;
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
xhci_exynos->xhci_alloc->offset = 0;
dev_info(&dev->dev, "WAKE UNLOCK\n");
#endif
__pm_relax(xhci_exynos->main_wakelock);
__pm_relax(xhci_exynos->shared_wakelock);
xhci_exynos_unregister_notify();
if (!rhdev || !srhdev)
goto remove_hcd;
/* check all ports */
for (timeout = 0; timeout < XHCI_HUB_EVENT_TIMEOUT; timeout++) {
need_wait = false;
usb_hub_for_each_child(rhdev, port, udev) {
if (udev && udev->devnum != -1)
need_wait = true;
}
if (need_wait == false) {
usb_hub_for_each_child(srhdev, port, udev) {
if (udev && udev->devnum != -1)
need_wait = true;
}
}
if (need_wait == true) {
usleep_range(20000, 22000);
timeout += 20;
xhci_info(xhci, "Waiting USB hub disconnect\n");
} else {
xhci_info(xhci, "device disconnect all done\n");
break;
}
}
remove_hcd:
#ifdef CONFIG_USB_DEBUG_DETAILED_LOG
dev_info(&dev->dev, "remove hcd (shared)\n");
#endif
usb_remove_hcd(shared_hcd);
xhci->shared_hcd = NULL;
usb_phy_shutdown(hcd->usb_phy);
/*
* In usb_remove_hcd, phy_exit is called if phy is not NULL.
* However, in the case that PHY was turn on or off as runtime PM,
* PHY sould not exit at this time. So, to prevent the PHY exit,
* PHY pointer have to be NULL.
*/
#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
if (parent && xhci_exynos->phy_usb2)
xhci_exynos->phy_usb2 = NULL;
if (parent && xhci_exynos->phy_usb3)
xhci_exynos->phy_usb3 = NULL;
#endif
#ifdef CONFIG_USB_DEBUG_DETAILED_LOG
dev_info(&dev->dev, "remove hcd (main)\n");
#endif
usb_remove_hcd(hcd);
xhci_vendor_cleanup(xhci);
wakeup_source_unregister(xhci_exynos->main_wakelock);
wakeup_source_unregister(xhci_exynos->shared_wakelock);
devm_iounmap(&dev->dev, hcd->regs);
usb_put_hcd(shared_hcd);
clk_disable_unprepare(clk);
clk_disable_unprepare(reg_clk);
usb_put_hcd(hcd);
pm_runtime_disable(&dev->dev);
pm_runtime_put_noidle(&dev->dev);
pm_runtime_set_suspended(&dev->dev);
return 0;
}
/*
static void xhci_exynos_shutdown(struct platform_device *dev)
{
struct xhci_hcd_exynos *xhci_exynos = platform_get_drvdata(dev);
struct usb_hcd *hcd = xhci_exynos->hcd;
platform_set_drvdata(dev, hcd);
usb_hcd_platform_shutdown(dev);
platform_set_drvdata(dev, xhci_exynos);
}
*/
extern u32 otg_is_connect(void);
static int __maybe_unused xhci_exynos_suspend(struct device *dev)
{
struct xhci_hcd_exynos *xhci_exynos = dev_get_drvdata(dev);
struct usb_hcd *hcd = xhci_exynos->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret = 0;
pr_info("[%s]\n", __func__);
if (otg_is_connect() == 1) { /* If it is OTG_CONNECT_ONLY */
ret = xhci_priv_suspend_quirk(hcd);
if (ret)
return ret;
/*
* xhci_suspend() needs `do_wakeup` to know whether host is allowed
* to do wakeup during suspend.
*/
pr_info("[%s]: xhci_suspend!\n", __func__);
ret = xhci_suspend(xhci, device_may_wakeup(dev));
} else { /* Enable HS ReWA */
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb2, 1, 0);
#ifdef CONFIG_EXYNOS_USBDRD_PHY30
/* Enable SS ReWA */
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb3, 1, 0);
#endif
is_rewa_enabled = 1;
}
usb_dr_role_control(0);
return ret;
}
static int __maybe_unused xhci_exynos_resume(struct device *dev)
{
struct xhci_hcd_exynos *xhci_exynos = dev_get_drvdata(dev);
struct usb_hcd *hcd = xhci_exynos->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret;
pr_info("[%s]\n", __func__);
ret = xhci_priv_resume_quirk(hcd);
if (ret)
return ret;
if (otg_is_connect() == 1) { /* If it is OTG_CONNECT_ONLY */
pr_info("[%s]: xhci_resume!\n", __func__);
ret = xhci_resume(xhci, 0);
if (ret)
return ret;
}
if (is_rewa_enabled == 1) {
#ifdef CONFIG_EXYNOS_USBDRD_PHY30
/* Disable SS ReWA */
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb3, 1, 1);
#endif
/* Disablee HS ReWA */
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb2, 1, 1);
exynos_usbdrd_phy_vendor_set(xhci_exynos->phy_usb2, 0, 0);
is_rewa_enabled = 0;
}
usb_dr_role_control(1);
return ret;
}
static const struct dev_pm_ops xhci_plat_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xhci_exynos_suspend, xhci_exynos_resume)
};
static const struct acpi_device_id usb_xhci_acpi_match[] = {
/* XHCI-compliant USB Controller */
{ "PNP0D10", },
{ }
};
MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
static struct platform_driver usb_xhci_driver = {
.probe = xhci_exynos_probe,
.remove = xhci_exynos_remove,
/* Host is supposed to removed in usb_reboot_noti.
Thats's why we don't need xhci_exynos_shutdown. */
//.shutdown = xhci_exynos_shutdown,
.driver = {
.name = "xhci-hcd-exynos",
.pm = &xhci_plat_pm_ops,
.of_match_table = of_match_ptr(usb_xhci_of_match),
.acpi_match_table = ACPI_PTR(usb_xhci_acpi_match),
.dev_groups = xhci_exynos_groups,
},
};
MODULE_ALIAS("platform:xhci-hcd-exynos");
static int __init xhci_exynos_init(void)
{
xhci_init_driver(&xhci_exynos_hc_driver, &xhci_exynos_overrides);
return platform_driver_register(&usb_xhci_driver);
}
module_init(xhci_exynos_init);
static void __exit xhci_exynos_exit(void)
{
platform_driver_unregister(&usb_xhci_driver);
}
module_exit(xhci_exynos_exit);
MODULE_DESCRIPTION("xHCI Exynos Platform Host Controller Driver");
MODULE_LICENSE("GPL");