blob: 0ffde5c352d34a717e430b2f1299b4ef966f3f30 [file] [log] [blame]
/**
* Copyright (C) 2012 Intel Corp.
* Author: Jiebing Li
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb/dwc3-intel-mid.h>
#include <linux/usb/phy.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
#include "otg.h"
#include "debug.h"
#include "core.c"
#include "ep0.c"
#include "gadget.c"
/* FLIS register */
#define APBFC_EXIOTG3_MISC0_REG 0xF90FF85C
/* Global User Control Register Auto Retry bit*/
#define DWC3_GUCTL_USB_HST_IN_AUTO_RETRY_EN (1 << 14)
/* Global Configuration Register */
#define DWC3_GRXTHRCFG_USBRXPKTCNTSEL (1 << 29)
#define DWC3_GRXTHRCFG_USBRXPKTCNT(n) (n << 24)
#define DWC3_GRXTHRCFG_USBRXPKTCNT_MASK (0xf << 24)
#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE(n) (n << 19)
#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_MASK (0x1f << 19)
/**
* struct dwc3_dev_data - Structure holding platform related
* information
* @flis_reg: FLIS register
* @grxthrcfg: DWC3 GRXTHCFG register
*/
struct dwc3_dev_data {
struct dwc3 *dwc;
void __iomem *flis_reg;
u32 grxthrcfg;
struct mutex mutex;
};
static struct dwc3_dev_data *_dev_data;
/*
* dwc3_set_fils_reg - set FLIS register
*
* This is a workaround for OTG3 IP bug of using EP #8 for host mode
*/
static void dwc3_set_flis_reg(void)
{
u32 reg;
void __iomem *flis_reg;
flis_reg = _dev_data->flis_reg;
reg = dwc3_readl(flis_reg, DWC3_GLOBALS_REGS_START);
reg &= ~(1 << 3);
dwc3_writel(flis_reg, DWC3_GLOBALS_REGS_START, reg);
}
/*
* dwc3_disable_multi_packet - set GRXTHRCFG register to disable
* reception multi-packet thresholdingfor DWC2.50a.
*/
static void dwc3_disable_multi_packet(struct dwc3 *dwc)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
_dev_data->grxthrcfg = reg;
if (reg) {
reg &= ~DWC3_GRXTHRCFG_USBRXPKTCNTSEL;
reg &= ~DWC3_GRXTHRCFG_USBRXPKTCNT_MASK;
reg &= ~DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_MASK;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
}
}
/*
* dwc3_enable_host_auto_retry - clear Auto Retry Enable bit
* for device mode
*/
static void dwc3_enable_host_auto_retry(struct dwc3 *dwc, bool enable)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
if (enable)
reg |= DWC3_GUCTL_USB_HST_IN_AUTO_RETRY_EN;
else
reg &= ~DWC3_GUCTL_USB_HST_IN_AUTO_RETRY_EN;
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
}
static void dwc3_do_extra_change(struct dwc3 *dwc)
{
dwc3_set_flis_reg();
if (dwc->revision == DWC3_REVISION_250A)
dwc3_disable_multi_packet(dwc);
dwc3_enable_host_auto_retry(dwc, false);
}
static void dwc3_enable_hibernation(struct dwc3 *dwc)
{
u32 num, reg;
if (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)
!= DWC3_GHWPARAMS1_EN_PWROPT_HIB) {
dev_err(dwc->dev, "Device Mode Hibernation is not supported\n");
return;
}
num = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(
dwc->hwparams.hwparams4);
if (num != 1)
dev_err(dwc->dev, "number of scratchpad buffer: %d\n", num);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_SET_SCRATCH_ADDR_LO,
dwc->scratch_array_dma & 0xffffffffU);
}
/*
* Re-write irq functions. Not use irq thread. Because irqthread has negative
* impact on usb performance, especially for usb network performance, USB3 UDP
* download performance will drop from 80MB/s to 40MB/s if irqthread is enabled.
*/
static irqreturn_t dwc3_quirks_process_event_buf(struct dwc3 *dwc, u32 buf)
{
struct dwc3_event_buffer *evt;
u32 count;
u32 reg;
int left;
evt = dwc->ev_buffs[buf];
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
return IRQ_NONE;
evt->count = count;
/* WORKAROUND: Add 4 us delay workaround to A-unit issue in A0 stepping.
* Can be removed after B0.
*/
if (dwc->is_otg && dwc->revision == DWC3_REVISION_210A)
udelay(4);
left = evt->count;
while (left > 0) {
union dwc3_event event;
event.raw = *(u32 *) (evt->buf + evt->lpos);
dwc3_process_event_entry(dwc, &event);
/*
* FIXME we wrap around correctly to the next entry as
* almost all entries are 4 bytes in size. There is one
* entry which has 12 bytes which is a regular entry
* followed by 8 bytes data. ATM I don't know how
* things are organized if we get next to the a
* boundary so I worry about that once we try to handle
* that.
*/
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
left -= 4;
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
}
evt->count = 0;
return IRQ_HANDLED;
}
static irqreturn_t dwc3_quirks_interrupt(int irq, void *_dwc)
{
struct dwc3 *dwc = _dwc;
int i;
irqreturn_t ret = IRQ_NONE;
spin_lock(&dwc->lock);
if (dwc->pm_state != PM_ACTIVE) {
if (dwc->pm_state == PM_SUSPENDED) {
dev_info(dwc->dev, "u2/u3 pmu is received\n");
pm_runtime_get(dwc->dev);
dwc->pm_state = PM_RESUMING;
ret = IRQ_HANDLED;
}
goto out;
}
for (i = 0; i < dwc->num_event_buffers; i++) {
irqreturn_t status;
status = dwc3_quirks_process_event_buf(dwc, i);
if (status == IRQ_HANDLED)
ret = status;
}
out:
spin_unlock(&dwc->lock);
return ret;
}
int dwc3_start_peripheral(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int irq;
int ret = 0;
pm_runtime_get_sync(dwc->dev);
mutex_lock(&_dev_data->mutex);
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver && dwc->soft_connected) {
spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_core_init(dwc);
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->hiber_enabled)
dwc3_enable_hibernation(dwc);
dwc3_do_extra_change(dwc);
dwc3_event_buffers_setup(dwc);
ret = dwc3_init_for_enumeration(dwc);
if (ret)
goto err1;
if (dwc->soft_connected)
dwc3_gadget_run_stop(dwc, 1);
}
dwc->pm_state = PM_ACTIVE;
spin_unlock_irqrestore(&dwc->lock, flags);
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
if (dwc->quirks_disable_irqthread)
ret = request_irq(irq, dwc3_quirks_interrupt,
IRQF_SHARED, "dwc3", dwc);
else
ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
IRQF_SHARED, "dwc3", dwc);
if (ret) {
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
irq, ret);
goto err0;
}
mutex_unlock(&_dev_data->mutex);
return 0;
err1:
spin_unlock_irqrestore(&dwc->lock, flags);
err0:
mutex_unlock(&_dev_data->mutex);
return ret;
}
int dwc3_stop_peripheral(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
u8 epnum;
int irq;
mutex_lock(&_dev_data->mutex);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_stop_active_transfers(dwc);
if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
dwc3_disconnect_gadget(dwc);
dwc->gadget.speed = USB_SPEED_UNKNOWN;
}
dwc->start_config_issued = false;
/* Clear Run/Stop bit */
dwc3_gadget_run_stop(dwc, 0);
dwc3_gadget_keep_conn(dwc, 0);
for (epnum = 0; epnum < 2; epnum++) {
struct dwc3_ep *dep;
dep = dwc->eps[epnum];
if (dep->flags & DWC3_EP_ENABLED)
__dwc3_gadget_ep_disable(dep);
}
dwc3_gadget_disable_irq(dwc);
dwc3_event_buffers_cleanup(dwc);
if (_dev_data->grxthrcfg && dwc->revision == DWC3_REVISION_250A) {
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, _dev_data->grxthrcfg);
_dev_data->grxthrcfg = 0;
}
dwc3_enable_host_auto_retry(dwc, true);
if (dwc->pm_state != PM_SUSPENDED)
pm_runtime_put(dwc->dev);
dwc->pm_state = PM_DISCONNECTED;
spin_unlock_irqrestore(&dwc->lock, flags);
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
free_irq(irq, dwc);
mutex_unlock(&_dev_data->mutex);
cancel_delayed_work_sync(&dwc->link_work);
return 0;
}
static int dwc3_device_gadget_pullup(struct usb_gadget *g, int is_on)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int ret;
/*
* FIXME If pm_state is PM_RESUMING, we should wait for it to
* become PM_ACTIVE before continue. The chance of hitting
* PM_RESUMING is rare, but if so, we'll return directly.
*
* If some gadget reaches here in atomic context,
* pm_runtime_get_sync will cause a sleep problem.
*/
if (dwc->pm_state == PM_RESUMING) {
dev_err(dwc->dev, "%s: PM_RESUMING, return -EIO\n", __func__);
return -EIO;
}
if (dwc->pm_state == PM_SUSPENDED)
pm_runtime_get_sync(dwc->dev);
is_on = !!is_on;
mutex_lock(&_dev_data->mutex);
if (dwc->soft_connected == is_on)
goto done;
dwc->soft_connected = is_on;
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->pm_state == PM_DISCONNECTED) {
spin_unlock_irqrestore(&dwc->lock, flags);
goto done;
}
if (is_on) {
/* Per dwc3 databook 2.40a section 8.1.9, re-connection
* should follow steps described section 8.1.1 power on
* or soft reset.
*/
spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_core_init(dwc);
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->hiber_enabled)
dwc3_enable_hibernation(dwc);
dwc3_do_extra_change(dwc);
dwc3_event_buffers_setup(dwc);
dwc3_init_for_enumeration(dwc);
ret = dwc3_gadget_run_stop(dwc, 1);
if (dwc->hiber_enabled)
dwc3_gadget_keep_conn(dwc, 1);
} else {
u8 epnum;
for (epnum = 0; epnum < 2; epnum++) {
struct dwc3_ep *dep;
dep = dwc->eps[epnum];
if (dep->flags & DWC3_EP_ENABLED)
__dwc3_gadget_ep_disable(dep);
}
dwc3_stop_active_transfers(dwc);
dwc3_gadget_keep_conn(dwc, 0);
ret = dwc3_gadget_run_stop(dwc, 0);
dwc3_gadget_disable_irq(dwc);
}
spin_unlock_irqrestore(&dwc->lock, flags);
mutex_unlock(&_dev_data->mutex);
return ret;
done:
mutex_unlock(&_dev_data->mutex);
return 0;
}
static const struct usb_gadget_ops dwc3_device_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
.pullup = dwc3_device_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
.vbus_draw = dwc3_vbus_draw,
};
static int dwc3_device_intel_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct dwc3 *dwc;
struct device *dev = &pdev->dev;
int ret = -ENOMEM;
void *mem;
struct dwc_device_par *pdata;
struct usb_phy *usb_phy;
struct dwc_otg2 *otg;
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
if (!mem) {
dev_err(dev, "not enough memory\n");
return -ENOMEM;
}
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem;
_dev_data = kzalloc(sizeof(*_dev_data), GFP_KERNEL);
if (!_dev_data) {
dev_err(dev, "not enough memory\n");
return -ENOMEM;
}
_dev_data->dwc = dwc;
pdata = (struct dwc_device_par *)pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data for %s.\n",
dev_name(&pdev->dev));
return -ENODEV;
}
if (node) {
dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
} else {
dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
}
if (IS_ERR(dwc->usb2_phy)) {
ret = PTR_ERR(dwc->usb2_phy);
/*
* if -ENXIO is returned, it means PHY layer wasn't
* enabled, so it makes no sense to return -EPROBE_DEFER
* in that case, since no PHY driver will ever probe.
*/
if (ret == -ENXIO)
return ret;
dev_err(dev, "no usb2 phy configured\n");
return -EPROBE_DEFER;
}
if (IS_ERR(dwc->usb3_phy)) {
ret = PTR_ERR(dwc->usb2_phy);
/*
* if -ENXIO is returned, it means PHY layer wasn't
* enabled, so it makes no sense to return -EPROBE_DEFER
* in that case, since no PHY driver will ever probe.
*/
if (ret == -ENXIO)
return ret;
dev_err(dev, "no usb3 phy configured\n");
return -EPROBE_DEFER;
}
mutex_init(&_dev_data->mutex);
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
dwc->regs = pdata->io_addr + DWC3_GLOBALS_REGS_START;
dwc->regs_size = pdata->len - DWC3_GLOBALS_REGS_START;
dwc->dev = dev;
dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
if (!strncmp("super", maximum_speed, 5))
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
else if (!strncmp("high", maximum_speed, 4))
dwc->maximum_speed = DWC3_DCFG_HIGHSPEED;
else if (!strncmp("full", maximum_speed, 4))
dwc->maximum_speed = DWC3_DCFG_FULLSPEED1;
else if (!strncmp("low", maximum_speed, 3))
dwc->maximum_speed = DWC3_DCFG_LOWSPEED;
else
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_get_sync(dev);
pm_runtime_forbid(dev);
dwc3_cache_hwparams(dwc);
dwc3_core_num_eps(dwc);
_dev_data->flis_reg =
ioremap_nocache(APBFC_EXIOTG3_MISC0_REG, 4);
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
goto err0;
}
/*
* Not use irq thread, because irqthread has negative impact
* on usb performance, especially for usb network performance.
*/
dwc->quirks_disable_irqthread = 1;
usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
otg = container_of(usb_phy, struct dwc_otg2, usb2_phy);
otg->start_device = dwc3_start_peripheral;
otg->stop_device = dwc3_stop_peripheral;
otg->vbus_draw = dwc3_vbus_draw;
usb_put_phy(usb_phy);
dwc->is_otg = 1;
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
goto err0;
}
dwc->gadget.ops = &dwc3_device_gadget_ops;
dwc->gadget.is_otg = 1;
dwc->mode = DWC3_MODE_DEVICE;
ret = dwc3_debugfs_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize debugfs\n");
goto err1;
}
pm_runtime_allow(dev);
pm_runtime_put(dev);
return 0;
err1:
dwc3_gadget_exit(dwc);
err0:
dwc3_free_event_buffers(dwc);
return ret;
}
static int dwc3_device_intel_remove(struct platform_device *pdev)
{
iounmap(_dev_data->flis_reg);
dwc3_remove(pdev);
kfree(_dev_data);
_dev_data = NULL;
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static const struct dev_pm_ops dwc3_device_pm_ops = {
.runtime_suspend = dwc3_runtime_suspend,
.runtime_resume = dwc3_runtime_resume,
};
#define DWC3_DEVICE_PM_OPS (&dwc3_device_pm_ops)
#else
#define DWC3_DEVICE_PM_OPS NULL
#endif
static struct platform_driver dwc3_device_intel_driver = {
.probe = dwc3_device_intel_probe,
.remove = dwc3_device_intel_remove,
.driver = {
.name = "dwc3-device",
.of_match_table = of_match_ptr(of_dwc3_match),
.pm = DWC3_DEVICE_PM_OPS,
},
};
module_platform_driver(dwc3_device_intel_driver);
MODULE_ALIAS("platform:dwc3");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");