linuxOS_D21X/source/linux-5.10/drivers/usb/gadget/udc/aic_udc.c
2025-08-14 15:17:16 +08:00

3910 lines
92 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021, Artinchip Technology Co., Ltd
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
#include <linux/usb/of.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/phy.h>
#include <linux/usb/composite.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include "aic_udc.h"
static inline u32 aic_readl(struct aic_usb_gadget *gg, u32 offset);
static inline void aic_writel(struct aic_usb_gadget *gg, u32 value,
u32 offset);
static void aic_ep0_enqueue_setup(struct aic_usb_gadget *gg);
static struct usb_request *aic_ep_alloc_request(struct usb_ep *ep,
gfp_t flags);
static void aic_ep_free_request(struct usb_ep *ep,
struct usb_request *req);
static void aic_ep_complete_request(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep,
struct aic_usb_req *a_req,
int result);
static int aic_ep_queue_request_nolock(struct usb_ep *ep,
struct usb_request *req,
gfp_t gfp_flags);
static int aic_ep_sethalt_nolock(struct usb_ep *ep, int value, bool now);
static void aic_ep_start_req(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep,
struct aic_usb_req *a_req,
bool continuing);
static void aic_ep_start_next_request(struct aic_usb_ep *a_ep);
static void aic_stall_ep0(struct aic_usb_gadget *gg);
static void aic_epint_handle_complete_in(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep);
static void aic_kill_ep_reqs(struct aic_usb_gadget *gg,
struct aic_usb_ep *ep,
int result);
static int aic_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc);
static int aic_ep_disable(struct usb_ep *ep);
static int aic_ep_disable_nolock(struct usb_ep *ep);
static int aic_set_test_mode(struct aic_usb_gadget *gg, int testmode);
#ifdef CONFIG_DEBUG_FS
#define print_param(_seq, _ptr, _param) \
seq_printf((_seq), "%-30s: %d\n", #_param, (_ptr)->_param)
#define print_param_hex(_seq, _ptr, _param) \
seq_printf((_seq), "%-30s: 0x%x\n", #_param, (_ptr)->_param)
#define dump_register(nm) \
{ \
.name = #nm, \
.offset = nm, \
}
static const struct debugfs_reg32 aic_udc_regs[] = {
dump_register(AHBBASIC),
dump_register(USBDEVINIT),
dump_register(USBPHYIF),
dump_register(USBINTSTS),
dump_register(USBINTMSK),
dump_register(RXFIFOSTS_DBG),
dump_register(RXFIFOSIZ),
dump_register(NPTXFIFOSIZ),
dump_register(NPTXFIFOSTS),
dump_register(USBULPIPHY),
dump_register(TXFIFOSIZ(1)),
dump_register(TXFIFOSIZ(2)),
dump_register(USBDEVCONF),
dump_register(USBDEVFUNC),
dump_register(USBLINESTS),
dump_register(INEPINTMSK),
dump_register(OUTEPINTMSK),
dump_register(USBEPINT),
dump_register(USBEPINTMSK),
dump_register(DTKNQR1),
dump_register(DTKNQR2),
dump_register(DTKNQR3),
dump_register(DTKNQR4),
dump_register(INEPCFG(0)),
dump_register(INEPCFG(1)),
dump_register(INEPCFG(2)),
dump_register(INEPCFG(3)),
dump_register(INEPCFG(4)),
dump_register(OUTEPCFG(0)),
dump_register(OUTEPCFG(1)),
dump_register(OUTEPCFG(2)),
dump_register(OUTEPCFG(3)),
dump_register(OUTEPCFG(4)),
dump_register(INEPINT(0)),
dump_register(INEPINT(1)),
dump_register(INEPINT(2)),
dump_register(INEPINT(3)),
dump_register(INEPINT(4)),
dump_register(OUTEPINT(0)),
dump_register(OUTEPINT(1)),
dump_register(OUTEPINT(2)),
dump_register(OUTEPINT(3)),
dump_register(OUTEPINT(4)),
dump_register(INEPTSFSIZ(0)),
dump_register(INEPTSFSIZ(1)),
dump_register(INEPTSFSIZ(2)),
dump_register(INEPTSFSIZ(3)),
dump_register(INEPTSFSIZ(4)),
dump_register(OUTEPTSFSIZ(0)),
dump_register(OUTEPTSFSIZ(1)),
dump_register(OUTEPTSFSIZ(2)),
dump_register(OUTEPTSFSIZ(3)),
dump_register(OUTEPTSFSIZ(4)),
dump_register(INEPDMAADDR(0)),
dump_register(INEPDMAADDR(1)),
dump_register(INEPDMAADDR(2)),
dump_register(INEPDMAADDR(3)),
dump_register(INEPDMAADDR(4)),
dump_register(OUTEPDMAADDR(0)),
dump_register(OUTEPDMAADDR(1)),
dump_register(OUTEPDMAADDR(2)),
dump_register(OUTEPDMAADDR(3)),
dump_register(OUTEPDMAADDR(4)),
dump_register(INEPTXSTS(0)),
dump_register(INEPTXSTS(1)),
dump_register(INEPTXSTS(2)),
dump_register(INEPTXSTS(3)),
dump_register(INEPTXSTS(4)),
dump_register(PCGCTL),
dump_register(UDCVERSION),
};
static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct aic_usb_gadget *gg = s->private;
unsigned long flags;
u32 testmode = 0;
char buf[32];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "test_j", 6))
testmode = USB_TEST_J;
else if (!strncmp(buf, "test_k", 6))
testmode = USB_TEST_K;
else if (!strncmp(buf, "test_se0_nak", 12))
testmode = USB_TEST_SE0_NAK;
else if (!strncmp(buf, "test_packet", 11))
testmode = USB_TEST_PACKET;
else if (!strncmp(buf, "test_force_enable", 17))
testmode = USB_TEST_FORCE_ENABLE;
else
testmode = 0;
spin_lock_irqsave(&gg->lock, flags);
aic_set_test_mode(gg, testmode);
spin_unlock_irqrestore(&gg->lock, flags);
return count;
}
static int testmode_show(struct seq_file *s, void *unused)
{
struct aic_usb_gadget *gg = s->private;
unsigned long flags;
int dctl;
spin_lock_irqsave(&gg->lock, flags);
dctl = aic_readl(gg, USBDEVFUNC);
dctl &= USBDEVFUNC_TSTCTL_MASK;
dctl >>= USBDEVFUNC_TSTCTL_SHIFT;
spin_unlock_irqrestore(&gg->lock, flags);
switch (dctl) {
case 0:
seq_puts(s, "no test\n");
break;
case USB_TEST_J:
seq_puts(s, "test_j\n");
break;
case USB_TEST_K:
seq_puts(s, "test_k\n");
break;
case USB_TEST_SE0_NAK:
seq_puts(s, "test_se0_nak\n");
break;
case USB_TEST_PACKET:
seq_puts(s, "test_packet\n");
break;
case USB_TEST_FORCE_ENABLE:
seq_puts(s, "test_force_enable\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", dctl);
}
return 0;
}
static int testmode_open(struct inode *inode, struct file *file)
{
return single_open(file, testmode_show, inode->i_private);
}
static const struct file_operations testmode_fops = {
.owner = THIS_MODULE,
.open = testmode_open,
.write = testmode_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ep_show(struct seq_file *seq, void *v)
{
struct aic_usb_ep *ep = seq->private;
struct aic_usb_gadget *gg = ep->parent;
struct aic_usb_req *req = NULL;
int index = ep->index;
int show_limit = 15;
unsigned long flags;
seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n",
ep->index, ep->ep.name, (ep->dir_in ? "in" : "out"));
/* first show the register state */
seq_printf(seq, "\tINEPCFG=0x%08x, OUTEPCFG=0x%08x\n",
aic_readl(gg, INEPCFG(index)),
aic_readl(gg, OUTEPCFG(index)));
seq_printf(seq, "\tINEPDMAADDR=0x%08x, OUTEPDMAADDR=0x%08x\n",
aic_readl(gg, INEPDMAADDR(index)),
aic_readl(gg, OUTEPDMAADDR(index)));
seq_printf(seq, "\tINEPINT=0x%08x, OUTEPINT=0x%08x\n",
aic_readl(gg, INEPINT(index)),
aic_readl(gg, OUTEPINT(index)));
seq_printf(seq, "\tINEPTSFSIZ=0x%08x, OUTEPTSFSIZ=0x%08x\n",
aic_readl(gg, INEPTSFSIZ(index)),
aic_readl(gg, OUTEPTSFSIZ(index)));
seq_puts(seq, "\n");
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
seq_printf(seq, "total_data=%ld\n", ep->total_data);
seq_printf(seq, "request list (%p,%p):\n",
ep->queue.next, ep->queue.prev);
spin_lock_irqsave(&gg->lock, flags);
list_for_each_entry(req, &ep->queue, queue) {
if (--show_limit < 0) {
seq_puts(seq, "not showing more requests...\n");
break;
}
seq_printf(seq, "%c req %p: %d bytes @%p, ",
req == ep->req ? '*' : ' ',
req, req->req.length, req->req.buf);
seq_printf(seq, "%d done, res %d\n",
req->req.actual, req->req.status);
}
spin_unlock_irqrestore(&gg->lock, flags);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ep);
static int fifo_show(struct seq_file *seq, void *v)
{
struct aic_usb_gadget *gg = seq->private;
u32 val;
int idx;
seq_puts(seq, "Non-periodic FIFOs:\n");
seq_printf(seq, "RXFIFO: Size %d\n", aic_readl(gg, RXFIFOSIZ));
val = aic_readl(gg, NPTXFIFOSIZ);
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
for (idx = 1; idx <= gg->params.num_perio_in_ep; idx++) {
val = aic_readl(gg, TXFIFOSIZ(idx));
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(fifo);
static int state_show(struct seq_file *seq, void *v)
{
struct aic_usb_gadget *gg = seq->private;
int idx;
seq_printf(seq, "USBDEVCONF=0x%08x, USBDEVFUNC=0x%08x, USBLINESTS=0x%08x\n",
aic_readl(gg, USBDEVCONF),
aic_readl(gg, USBDEVFUNC),
aic_readl(gg, USBLINESTS));
seq_printf(seq, "INEPINTMSK=0x%08x, DOEPMASK=0x%08x\n",
aic_readl(gg, INEPINTMSK), aic_readl(gg, OUTEPINTMSK));
seq_printf(seq, "USBINTMSK=0x%08x, USBINTSTS=0x%08x\n",
aic_readl(gg, USBINTMSK),
aic_readl(gg, USBINTSTS));
seq_printf(seq, "USBEPINTMSK=0x%08x, USBEPINT=0x%08x\n",
aic_readl(gg, USBEPINTMSK),
aic_readl(gg, USBEPINT));
seq_printf(seq, "NPTXFIFOSTS=0x%08x, RXFIFOSTS_DBG=%08x\n",
aic_readl(gg, NPTXFIFOSTS),
aic_readl(gg, RXFIFOSTS_DBG));
seq_puts(seq, "\nEndpoint status:\n");
for (idx = 0; idx < gg->params.num_ep; idx++) {
u32 in, out;
in = aic_readl(gg, INEPCFG(idx));
out = aic_readl(gg, OUTEPCFG(idx));
seq_printf(seq, "ep%d: INEPCFG=0x%08x, OUTEPCFG=0x%08x",
idx, in, out);
in = aic_readl(gg, INEPTSFSIZ(idx));
out = aic_readl(gg, OUTEPTSFSIZ(idx));
seq_printf(seq, ", INEPTSFSIZ=0x%08x, OUTEPTSFSIZ=0x%08x",
in, out);
seq_puts(seq, "\n");
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(state);
static int params_show(struct seq_file *seq, void *v)
{
struct aic_usb_gadget *gg = seq->private;
struct aic_gadget_params *p = &gg->params;
int i;
print_param(seq, p, num_ep);
print_param(seq, p, num_perio_in_ep);
print_param(seq, p, total_fifo_size);
print_param(seq, p, rx_fifo_size);
print_param(seq, p, np_tx_fifo_size);
for (i = 1; i <= PERIOD_IN_EP_NUM; i++) {
char str[32];
snprintf(str, 32, "p_tx_fifo_size[%d]", i);
seq_printf(seq, "%-30s: %d\n", str, p->p_tx_fifo_size[i]);
}
print_param(seq, p, ep_dirs);
print_param(seq, p, speed);
print_param(seq, p, phy_type);
print_param(seq, p, phy_ulpi_ddr);
print_param(seq, p, phy_utmi_width);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(params);
int aic_udc_debugfs_init(struct aic_usb_gadget *gg)
{
struct dentry *root = NULL;
int i = 0;
int ret = 0;
root = debugfs_create_dir(dev_name(gg->dev), NULL);
gg->debug_root = root;
debugfs_create_file("params", 0444, root, gg, &params_fops);
/* create general state file */
debugfs_create_file("state", 0444, root, gg, &state_fops);
debugfs_create_file("fifo", 0444, root, gg, &fifo_fops);
debugfs_create_file("testmode", 0644, root, gg, &testmode_fops);
/* Create one file for each out endpoint */
for (i = 0; i < gg->params.num_ep; i++) {
struct aic_usb_ep *ep;
ep = gg->eps_out[i];
if (ep)
debugfs_create_file(ep->name, 0444, root, ep, &ep_fops);
}
/* Create one file for each in endpoint. EP0 is handled with out eps */
for (i = 1; i < gg->params.num_ep; i++) {
struct aic_usb_ep *ep;
ep = gg->eps_in[i];
if (ep)
debugfs_create_file(ep->name, 0444, root, ep, &ep_fops);
}
/* regdump */
gg->regset = devm_kzalloc(gg->dev, sizeof(*gg->regset),
GFP_KERNEL);
if (!gg->regset) {
ret = -ENOMEM;
goto err;
}
gg->regset->regs = aic_udc_regs;
gg->regset->nregs = ARRAY_SIZE(aic_udc_regs);
gg->regset->base = gg->regs;
debugfs_create_regset32("regdump", 0444, root, gg->regset);
return 0;
err:
debugfs_remove_recursive(gg->debug_root);
return ret;
}
void aic_udc_debugfs_exit(struct aic_usb_gadget *gg)
{
debugfs_remove_recursive(gg->debug_root);
gg->debug_root = NULL;
}
#else
static inline int aic_udc_debugfs_init(struct aic_usb_gadget *gg)
{ return 0; }
static inline void aic_udc_debugfs_exit(struct aic_usb_gadget *gg)
{ }
#endif
static inline struct aic_usb_req *our_req(struct usb_request *req)
{
return container_of(req, struct aic_usb_req, req);
}
static inline struct aic_usb_ep *our_ep(struct usb_ep *ep)
{
return container_of(ep, struct aic_usb_ep, ep);
}
static inline struct aic_usb_gadget *our_gadget(struct usb_gadget *gadget)
{
return container_of(gadget, struct aic_usb_gadget, gadget);
}
static inline u32 aic_readl(struct aic_usb_gadget *gg, u32 offset)
{
return readl(gg->regs + offset);
}
static inline void aic_writel(struct aic_usb_gadget *gg, u32 value,
u32 offset)
{
writel(value, gg->regs + offset);
}
static inline void aic_readl_rep(struct aic_usb_gadget *gg, u32 offset,
void *buffer, unsigned int count)
{
if (count) {
u32 *buf = buffer;
do {
u32 x = aic_readl(gg, offset);
*buf++ = x;
} while (--count);
}
}
static inline void aic_writel_rep(struct aic_usb_gadget *gg, u32 offset,
const void *buffer, unsigned int count)
{
if (count) {
const u32 *buf = buffer;
do {
aic_writel(gg, *buf++, offset);
} while (--count);
}
}
static inline void aic_set_bit(struct aic_usb_gadget *gg, u32 offset, u32 val)
{
aic_writel(gg, aic_readl(gg, offset) | val, offset);
}
static inline void aic_clear_bit(struct aic_usb_gadget *gg, u32 offset, u32 val)
{
aic_writel(gg, aic_readl(gg, offset) & ~val, offset);
}
int aic_wait_bit_clear(struct aic_usb_gadget *gg, u32 offset, u32 mask,
u32 timeout)
{
u32 i;
for (i = 0; i < timeout; i++) {
if (!(aic_readl(gg, offset) & mask))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
int aic_wait_bit_set(struct aic_usb_gadget *gg, u32 offset, u32 mask,
u32 timeout)
{
u32 i;
for (i = 0; i < timeout; i++) {
if (aic_readl(gg, offset) & mask)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void aic_en_gsint(struct aic_usb_gadget *gg, u32 ints)
{
u32 gsintmsk = aic_readl(gg, USBINTMSK);
u32 new_gsintmsk;
new_gsintmsk = gsintmsk | ints;
if (new_gsintmsk != gsintmsk) {
dev_dbg(gg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk);
aic_writel(gg, new_gsintmsk, USBINTMSK);
}
}
static void aic_dis_gsint(struct aic_usb_gadget *gg, u32 ints)
{
u32 gsintmsk = aic_readl(gg, USBINTMSK);
u32 new_gsintmsk;
new_gsintmsk = gsintmsk & ~ints;
if (new_gsintmsk != gsintmsk)
aic_writel(gg, new_gsintmsk, USBINTMSK);
}
static void aic_ctrl_epint(struct aic_usb_gadget *gg,
unsigned int ep, unsigned int dir_in,
unsigned int en)
{
unsigned long flags;
u32 bit = 1 << ep;
u32 daint;
if (!dir_in)
bit <<= 16;
local_irq_save(flags);
daint = aic_readl(gg, USBEPINTMSK);
if (en)
daint |= bit;
else
daint &= ~bit;
aic_writel(gg, daint, USBEPINTMSK);
local_irq_restore(flags);
}
static int aic_low_hw_enable(struct aic_usb_gadget *gg)
{
int ret = 0;
int i = 0;
/* enable regulator */
/* enable clock */
for (i = 0; i < USB_MAX_CLKS_RSTS && gg->clks[i]; i++)
ret = clk_prepare_enable(gg->clks[i]);
/* wait USB-PHY work stable */
udelay(200);
/* reset deassert */
reset_control_deassert(gg->reset);
reset_control_deassert(gg->reset_ecc);
udelay(200);
/* enable phy */
if (gg->uphy) {
ret = usb_phy_init(gg->uphy);
} else {
ret = phy_power_on(gg->phy);
if (ret == 0)
ret = phy_init(gg->phy);
}
return ret;
}
static int aic_low_hw_disable(struct aic_usb_gadget *gg)
{
int ret = 0;
int i = 0;
/* disable phy */
if (gg->uphy) {
usb_phy_shutdown(gg->uphy);
} else {
ret = phy_exit(gg->phy);
if (ret == 0)
ret = phy_power_off(gg->phy);
else
return ret;
}
/* reset assert */
reset_control_assert(gg->reset);
reset_control_assert(gg->reset_ecc);
/* disable clock */
for (i = 0; i < USB_MAX_CLKS_RSTS && gg->clks[i]; i++)
clk_disable_unprepare(gg->clks[i]);
/* disable regulator */
return ret;
}
static u32 aic_read_frameno(struct aic_usb_gadget *gg)
{
u32 reg = 0;
reg = aic_readl(gg, USBLINESTS);
reg &= USBLINESTS_SOFFN_MASK;
reg >>= USBLINESTS_SOFFN_SHIFT;
return reg;
}
static bool aic_target_frame_elapsed(struct aic_usb_ep *a_ep)
{
struct aic_usb_gadget *gg = a_ep->parent;
u32 target_frame = a_ep->target_frame;
u32 current_frame = gg->frame_number;
bool frame_overrun = a_ep->frame_overrun;
if (!frame_overrun && current_frame >= target_frame)
return true;
if (frame_overrun && current_frame >= target_frame &&
((current_frame - target_frame) < USBLINESTS_SOFFN_LIMIT / 2))
return true;
return false;
}
static inline void aic_incr_frame_num(struct aic_usb_ep *a_ep)
{
a_ep->target_frame += a_ep->interval;
if (a_ep->target_frame > USBLINESTS_SOFFN_LIMIT) {
a_ep->frame_overrun = true;
a_ep->target_frame &= USBLINESTS_SOFFN_LIMIT;
} else {
a_ep->frame_overrun = false;
}
}
static void aic_set_nextep(struct aic_usb_gadget *gg)
{
int i;
u32 reg = 0;
/* dma to set the next-endpoint pointer. */
for (i = 0; i < gg->params.num_ep; i++) {
u32 next = EPCTL_NEXTEP((i + 1) % 15);
if (gg->eps_in[i]) {
reg = aic_readl(gg, INEPCFG(i));
reg &= ~EPCTL_NEXTEP_MASK;
reg |= next;
aic_writel(gg, reg, INEPCFG(i));
}
}
}
static int aic_core_rst(struct aic_usb_gadget *gg)
{
u32 reset;
/* Core Soft Reset */
reset = aic_readl(gg, USBDEVINIT);
reset |= USBDEVINIT_CSFTRST;
aic_writel(gg, reset, USBDEVINIT);
if (aic_wait_bit_clear(gg, USBDEVINIT, USBDEVINIT_CSFTRST, 10000)) {
dev_warn(gg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
__func__);
return -EBUSY;
}
/* Wait for AHB master IDLE state */
if (aic_wait_bit_set(gg, AHBBASIC, AHBBASIC_AHBIDLE, 10000)) {
dev_warn(gg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n",
__func__);
return -EBUSY;
}
return 0;
}
static int aic_hs_phy_init(struct aic_usb_gadget *gg)
{
u32 phyif, phyif_old;
int ret = 0;
phyif = aic_readl(gg, USBPHYIF);
phyif_old = phyif;
switch (gg->params.phy_type) {
case AIC_PHY_TYPE_PARAM_ULPI:
/* ULPI interface */
dev_dbg(gg->dev, "HS ULPI PHY selected\n");
phyif |= USBPHYIF_ULPI_UTMI_SEL;
phyif &= ~(USBPHYIF_PHYIF16 | USBPHYIF_DDRSEL);
if (gg->params.phy_ulpi_ddr)
phyif |= USBPHYIF_DDRSEL;
break;
case AIC_PHY_TYPE_PARAM_UTMI:
/* UTMI+ interface */
dev_dbg(gg->dev, "HS UTMI+ PHY selected\n");
phyif &= ~(USBPHYIF_ULPI_UTMI_SEL | USBPHYIF_PHYIF16);
if (gg->params.phy_utmi_width == 16)
phyif |= USBPHYIF_PHYIF16;
phyif &= ~USBPHYIF_USBTRDTIM_MASK;
if (gg->params.phy_utmi_width == 16)
phyif |= 5 << USBPHYIF_USBTRDTIM_SHIFT;
else
phyif |= 9 << USBPHYIF_USBTRDTIM_SHIFT;
break;
default:
dev_err(gg->dev, "FS PHY selected at HS!\n");
break;
}
if (phyif != phyif_old) {
aic_writel(gg, phyif, USBPHYIF);
/* Reset after setting the PHY parameters */
ret = aic_core_rst(gg);
if (ret) {
dev_err(gg->dev,
"%s: Reset failed, aborting", __func__);
return ret;
}
}
return ret;
}
static int aic_init_fifo(struct aic_usb_gadget *gg)
{
u32 addr = 0;
u32 val = 0;
int i = 0;
/* reset fifo map of endpoints */
WARN_ON(gg->fifo_map);
gg->fifo_map = 0;
/* rx fifo */
aic_writel(gg, gg->params.rx_fifo_size, RXFIFOSIZ);
/* tx no-period fifo */
aic_writel(gg, (gg->params.rx_fifo_size <<
FIFOSIZE_STARTADDR_SHIFT) |
(gg->params.np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT),
NPTXFIFOSIZ);
/* tx period fifos */
addr = gg->params.rx_fifo_size + gg->params.np_tx_fifo_size;
for (i = 1; i <= gg->params.num_perio_in_ep; i++) {
val = addr;
val |= gg->params.p_tx_fifo_size[i] << FIFOSIZE_DEPTH_SHIFT;
aic_writel(gg, val, TXFIFOSIZ(i));
WARN_ONCE(addr + gg->params.p_tx_fifo_size[i] >
gg->params.total_fifo_size,
"insufficient fifo memory");
addr += gg->params.p_tx_fifo_size[i];
}
/* flush all fifo */
val = aic_readl(gg, USBDEVINIT);
val &= ~USBDEVINIT_TXFNUM_MASK;
val |= USBDEVINIT_TXFNUM(0x10);
val |= USBDEVINIT_TXFFLSH | USBDEVINIT_RXFFLSH;
aic_writel(gg, val, USBDEVINIT);
if (aic_wait_bit_clear(gg, USBDEVINIT,
(USBDEVINIT_TXFFLSH | USBDEVINIT_RXFFLSH),
100)) {
dev_warn(gg->dev, "%s: flush fifo timeout\n", __func__);
return -EBUSY;
}
dev_dbg(gg->dev, "FIFOs reset.\n");
return 0;
}
void aic_flush_tx_fifo(struct aic_usb_gadget *gg, const int num)
{
u32 rst;
dev_dbg(gg->dev, "Flush Tx FIFO %d\n", num);
/* Wait for AHB master IDLE state */
if (aic_wait_bit_set(gg, AHBBASIC, AHBBASIC_AHBIDLE, 10000))
dev_warn(gg->dev, "%s: HANG! AHB Idle GRSCTL\n",
__func__);
rst = aic_readl(gg, USBDEVINIT);
rst &= ~USBDEVINIT_TXFNUM_MASK;
rst |= (num << USBDEVINIT_TXFNUM_SHIFT) & USBDEVINIT_TXFNUM_MASK;
rst |= USBDEVINIT_TXFFLSH;
aic_writel(gg, rst, USBDEVINIT);
if (aic_wait_bit_clear(gg, USBDEVINIT, USBDEVINIT_TXFFLSH, 10000))
dev_warn(gg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n",
__func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
void aic_flush_rx_fifo(struct aic_usb_gadget *gg)
{
u32 rst;
dev_dbg(gg->dev, "%s()\n", __func__);
/* Wait for AHB master IDLE state */
if (aic_wait_bit_set(gg, AHBBASIC, AHBBASIC_AHBIDLE, 10000))
dev_warn(gg->dev, "%s: HANG! AHB Idle GRSCTL\n",
__func__);
rst = aic_readl(gg, USBDEVINIT);
rst |= USBDEVINIT_RXFFLSH;
aic_writel(gg, rst, USBDEVINIT);
/* Wait for RxFIFO flush done */
if (aic_wait_bit_clear(gg, USBDEVINIT, USBDEVINIT_RXFFLSH, 10000))
dev_warn(gg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_RXFFLSH\n",
__func__);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
static void aic_soft_disconnect(struct aic_usb_gadget *gg)
{
/* set the soft-disconnect bit */
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_SFTDISCON);
}
static void aic_soft_connect(struct aic_usb_gadget *gg)
{
/* remove the soft-disconnect and let's go */
aic_clear_bit(gg, USBDEVFUNC, USBDEVFUNC_SFTDISCON);
}
static u32 aic_read_ep_interrupts(struct aic_usb_gadget *gg,
unsigned int idx, int dir_in)
{
u32 msk_addr = dir_in ? INEPINTMSK : OUTEPINTMSK;
u32 int_addr = dir_in ? INEPINT(idx) : OUTEPINT(idx);
u32 ints;
u32 mask;
mask = aic_readl(gg, msk_addr);
mask |= EPINT_SETUP_RCVD;
ints = aic_readl(gg, int_addr);
ints &= mask;
return ints;
}
static int aic_set_test_mode(struct aic_usb_gadget *gg, int testmode)
{
int ctl = aic_readl(gg, USBDEVFUNC);
ctl &= ~USBDEVFUNC_TSTCTL_MASK;
switch (testmode) {
case USB_TEST_J:
case USB_TEST_K:
case USB_TEST_SE0_NAK:
case USB_TEST_PACKET:
case USB_TEST_FORCE_ENABLE:
ctl |= testmode << USBDEVFUNC_TSTCTL_SHIFT;
break;
default:
return -EINVAL;
}
aic_writel(gg, ctl, USBDEVFUNC);
return 0;
}
static u32 aic_ep0_mps(unsigned int mps)
{
switch (mps) {
case 64:
return EP0CTL_MPS_64;
case 32:
return EP0CTL_MPS_32;
case 16:
return EP0CTL_MPS_16;
case 8:
return EP0CTL_MPS_8;
}
/* bad max packet size, warn and return invalid result */
WARN_ON(1);
return (u32)-1;
}
static inline struct aic_usb_ep *index_to_ep(struct aic_usb_gadget *gg,
u32 index, u32 dir_in)
{
if (dir_in)
return gg->eps_in[index];
else
return gg->eps_out[index];
}
static struct aic_usb_ep *windex_to_ep(struct aic_usb_gadget *gg,
u32 windex)
{
struct aic_usb_ep *a_ep;
int dir = (windex & USB_DIR_IN) ? 1 : 0;
int idx = windex & 0x7F;
if (windex >= 0x100)
return NULL;
if (idx > gg->params.num_ep)
return NULL;
a_ep = index_to_ep(gg, idx, dir);
if (idx && a_ep->dir_in != dir)
return NULL;
return a_ep;
}
static bool on_list(struct aic_usb_ep *ep, struct aic_usb_req *test)
{
struct aic_usb_req *req = NULL, *treq;
list_for_each_entry_safe(req, treq, &ep->queue, queue) {
if (req == test)
return true;
}
return false;
}
static struct aic_usb_req *get_ep_head(struct aic_usb_ep *a_ep)
{
return list_first_entry_or_null(&a_ep->queue, struct aic_usb_req,
queue);
}
static unsigned int get_ep_limit(struct aic_usb_ep *a_ep)
{
int index = a_ep->index;
unsigned int maxsize;
unsigned int maxpkt;
if (index != 0) {
maxsize = EPTSIZ_XFERSIZE_LIMIT + 1;
maxpkt = EPTSIZ_PKTCNT_LIMIT + 1;
} else {
maxsize = 64 + 64;
if (a_ep->dir_in)
maxpkt = INEPTSFSIZ0_PKTCNT_LIMIT + 1;
else
maxpkt = 2;
}
/* we made the constant loading easier above by using +1 */
maxpkt--;
maxsize--;
/*
* constrain by packet count if maxpkts*pktsize is greater
* than the length register size.
*/
if ((maxpkt * a_ep->ep.maxpacket) < maxsize)
maxsize = maxpkt * a_ep->ep.maxpacket;
return maxsize;
}
static void aic_set_ep_maxpacket(struct aic_usb_gadget *gg,
unsigned int ep, unsigned int mps,
unsigned int mc, unsigned int dir_in)
{
struct aic_usb_ep *a_ep;
u32 reg;
a_ep = index_to_ep(gg, ep, dir_in);
if (!a_ep)
return;
if (ep == 0) {
u32 mps_bytes = mps;
/* EP0 is a special case */
mps = aic_ep0_mps(mps_bytes);
if (mps > 3)
goto bad_mps;
a_ep->ep.maxpacket = mps_bytes;
a_ep->mc = 1;
} else {
if (mps > 1024)
goto bad_mps;
a_ep->mc = mc;
if (mc > 3)
goto bad_mps;
a_ep->ep.maxpacket = mps;
}
if (dir_in) {
reg = aic_readl(gg, INEPCFG(ep));
reg &= ~EPCTL_MPS_MASK;
reg |= mps;
aic_writel(gg, reg, INEPCFG(ep));
} else {
reg = aic_readl(gg, OUTEPCFG(ep));
reg &= ~EPCTL_MPS_MASK;
reg |= mps;
aic_writel(gg, reg, OUTEPCFG(ep));
}
return;
bad_mps:
dev_err(gg->dev, "ep%d: bad mps of %d\n", ep, mps);
}
static void aic_gg_set_usb_res(void __iomem *ctl_reg, u32 resis)
{
u32 val;
if (ctl_reg == NULL)
return;
resis &= SYSCFG_USB_RES_CAL_VAL_MASK;
val = readl(ctl_reg);
val &= ~SYSCFG_USB_RES_CAL_VAL_MASK;
val |= resis << SYSCFG_USB_RES_CAL_VAL_SHIFT;
val |= 1 << SYSCFG_USB_RES_CAL_EN_SHIFT;
writel(val, ctl_reg);
}
static int aic_gg_get_res_cfg(struct device_node *np, struct aic_usb_res_cfg *cfg,
const char *property)
{
int len, index, offset, res;
const __be32 *prop;
struct device_node *child_np;
prop = of_get_property(np, property, &len);
if (!prop || len < 4 * sizeof(__be32))
return -ENODEV;
child_np = of_find_node_by_phandle(be32_to_cpup(prop++));
if (!child_np)
return -ENODEV;
index = be32_to_cpup(prop++);
offset = be32_to_cpup(prop++);
res = be32_to_cpup(prop);
cfg->addr = of_iomap(child_np, index);
if (!cfg->addr)
return -ENOMEM;
cfg->addr += offset;
cfg->resis = res;
pr_debug("device property : %s \n", property);
pr_debug("child_np : %s \n", child_np->name);
pr_debug("offset : %#x res : %#x \n", offset, res);
return 0;
}
static int aic_core_hw_init1(struct aic_usb_gadget *gg, bool is_usb_reset)
{
u32 reg = 0;
int i = 0;
int ret = 0;
if (!is_usb_reset) {
/* unmask subset of endpoint interrupts */
aic_writel(gg, INEPINTMSK_TIMEOUTMSK | INEPINTMSK_AHBERRMSK |
INEPINTMSK_EPDISBLDMSK | INEPINTMSK_XFERCOMPLMSK,
INEPINTMSK);
aic_writel(gg, OUTEPINTMSK_SETUPMSK | OUTEPINTMSK_AHBERRMSK |
OUTEPINTMSK_EPDISBLDMSK | OUTEPINTMSK_XFERCOMPLMSK,
OUTEPINTMSK);
/* mask all endpoint interrupt */
aic_writel(gg, 0, USBEPINTMSK);
/* soft disconnect */
aic_writel(gg, USBDEVFUNC, USBDEVFUNC_SFTDISCON);
/* enable DMA */
aic_set_bit(gg, USBDEVINIT, USBDEVINIT_DMA_EN);
}
/* Kill any ep0 requests as controller will be reinitialized */
aic_kill_ep_reqs(gg, gg->eps_out[0], -ECONNRESET);
if (!is_usb_reset) {
/* core reset */
ret = aic_core_rst(gg);
if (ret) {
dev_err(gg->dev,
"%s: Reset failed, aborting", __func__);
return ret;
}
} else {
/* all endpoints should be shutdown */
for (i = 1; i < gg->params.num_ep; i++) {
if (gg->eps_in[i])
aic_ep_disable_nolock(&gg->eps_in[i]->ep);
if (gg->eps_out[i])
aic_ep_disable_nolock(&gg->eps_out[i]->ep);
}
}
/* HS/FS timeout calibration */
reg = aic_readl(gg, USBPHYIF);
reg &= ~USBPHYIF_TOUTCAL_MASK;
reg |= USBPHYIF_TOUTCAL(7);
aic_writel(gg, reg, USBPHYIF);
return ret;
}
static void aic_core_hw_init2(struct aic_usb_gadget *gg, bool is_usb_reset)
{
u32 reg = 0;
if (!is_usb_reset) {
/* soft disconnect */
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_SFTDISCON);
}
/* device speed */
reg = USBDEVCONF_EPMISCNT(1);
switch (gg->params.speed) {
case AIC_SPEED_PARAM_LOW:
reg |= USBDEVCONF_DEVSPD_LS;
break;
case AIC_SPEED_PARAM_FULL:
if (gg->params.phy_type == AIC_PHY_TYPE_PARAM_FS)
reg |= USBDEVCONF_DEVSPD_FS48;
else
reg |= USBDEVCONF_DEVSPD_FS;
break;
default:
reg |= USBDEVCONF_DEVSPD_HS;
}
aic_writel(gg, reg, USBDEVCONF);
/* clear any pending interrupts */
aic_writel(gg, 0xffffffff, USBINTSTS);
/* set global interrupt mask */
reg = USBINTSTS_ERLYSUSP |
USBINTSTS_GOUTNAKEFF | USBINTSTS_GINNAKEFF |
USBINTSTS_USBRST | USBINTSTS_ENUMDONE |
USBINTSTS_USBSUSP | USBINTSTS_WKUPINT;
reg |= USBINTSTS_INCOMPL_SOIN | USBINTSTS_INCOMPL_SOOUT;
aic_writel(gg, reg, USBINTMSK);
/* AHB config */
reg = USBDEVINIT_GLBL_INTR_EN | USBDEVINIT_DMA_EN;
reg |= USBDEVINIT_HBSTLEN_INCR4 << USBDEVINIT_HBSTLEN_SHIFT;
aic_writel(gg, reg, USBDEVINIT);
/* set IN endpoint interrupt mask */
reg = INEPINTMSK_EPDISBLDMSK | INEPINTMSK_XFERCOMPLMSK |
INEPINTMSK_TIMEOUTMSK | INEPINTMSK_AHBERRMSK |
INEPINTMSK_INTKNEPMISMSK;
aic_writel(gg, reg, INEPINTMSK);
/* set OUT endpoint interrupt mask */
reg = OUTEPINTMSK_XFERCOMPLMSK | OUTEPINTMSK_STSPHSERCVDMSK |
OUTEPINTMSK_EPDISBLDMSK | OUTEPINTMSK_AHBERRMSK |
OUTEPINTMSK_SETUPMSK;
aic_writel(gg, reg, OUTEPINTMSK);
/* mask all endpoint interrupt */
aic_writel(gg, 0, USBEPINTMSK);
dev_dbg(gg->dev, "EP0: INEPCFG0=0x%08x, OUTEPCFG0=0x%08x\n",
aic_readl(gg, INEPCFG0),
aic_readl(gg, OUTEPCFG0));
/* enable in and out endpoint interrupts */
aic_en_gsint(gg, USBINTSTS_OEPINT | USBINTSTS_IEPINT);
/* Enable interrupts for EP0 in and out */
aic_ctrl_epint(gg, 0, 0, 1);
aic_ctrl_epint(gg, 0, 1, 1);
gg->tx_fifo_map |= 1;
if (!is_usb_reset) {
/* power-on programming done */
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_PWRONPRGDONE);
udelay(10);
aic_clear_bit(gg, USBDEVFUNC, USBDEVFUNC_PWRONPRGDONE);
}
dev_dbg(gg->dev, "USBDEVFUNC=0x%08x\n", aic_readl(gg, USBDEVFUNC));
/* ep0 OUT: set to read 1 8byte packet */
aic_writel(gg, EPTSIZ_MC(1) | EPTSIZ_PKTCNT(1) |
EPTSIZ_XFERSIZE(8), OUTEPTSFSIZ0);
/* ep0 OUT: enable + active */
aic_writel(gg, aic_ep0_mps(gg->eps_out[0]->ep.maxpacket) |
EPCTL_CNAK | EPCTL_EPENA |
EPCTL_USBACTEP,
OUTEPCFG0);
/* ep0 IN: disable + active */
aic_writel(gg, aic_ep0_mps(gg->eps_out[0]->ep.maxpacket) |
EPCTL_USBACTEP, INEPCFG0);
/* clear global NAKs */
reg = USBDEVFUNC_CGOUTNAK | USBDEVFUNC_CGNPINNAK;
if (!is_usb_reset)
reg |= USBDEVFUNC_SFTDISCON;
aic_set_bit(gg, USBDEVFUNC, reg);
mdelay(3);
dev_dbg(gg->dev, "EP0: INEPCFG0=0x%08x, OUTEPCFG0=0x%08x\n",
aic_readl(gg, INEPCFG0),
aic_readl(gg, OUTEPCFG0));
}
static int aic_core_init(struct aic_usb_gadget *gg,
bool is_usb_reset)
{
int ret = 0;
aic_gg_set_usb_res(gg->params.usb_res_cfg.addr, gg->params.usb_res_cfg.resis);
ret = aic_core_hw_init1(gg, is_usb_reset);
if (ret)
return ret;
ret = aic_hs_phy_init(gg);
if (ret)
return ret;
ret = aic_init_fifo(gg);
if (ret)
return ret;
aic_core_hw_init2(gg, is_usb_reset);
aic_set_nextep(gg);
aic_ep0_enqueue_setup(gg);
return 0;
}
static void aic_kill_ep_reqs(struct aic_usb_gadget *gg,
struct aic_usb_ep *ep,
int result)
{
unsigned int size;
ep->req = NULL;
while (!list_empty(&ep->queue)) {
struct aic_usb_req *req = get_ep_head(ep);
aic_ep_complete_request(gg, ep, req, result);
}
size = (aic_readl(gg, INEPTXSTS(ep->fifo_index)) & 0xffff) * 4;
if (size < ep->fifo_size)
aic_flush_tx_fifo(gg, ep->fifo_index);
}
void aic_core_disconnect(struct aic_usb_gadget *gg)
{
unsigned int i;
if (!gg->connected)
return;
gg->connected = 0;
gg->test_mode = 0;
/* all endpoints should be shutdown */
for (i = 0; i < gg->params.num_ep; i++) {
if (gg->eps_in[i])
aic_kill_ep_reqs(gg, gg->eps_in[i],
-ESHUTDOWN);
if (gg->eps_out[i])
aic_kill_ep_reqs(gg, gg->eps_out[i],
-ESHUTDOWN);
}
aic_gadget_driver_cb(gg, disconnect);
usb_gadget_set_state(&gg->gadget, USB_STATE_NOTATTACHED);
}
static void aic_ep_complete_empty(struct usb_ep *ep,
struct usb_request *req)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
dev_dbg(gg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
aic_ep_free_request(ep, req);
}
static int aic_ep0_enqueue_reply(struct aic_usb_gadget *gg,
struct aic_usb_ep *ep,
void *buff,
int length)
{
struct usb_request *req;
int ret;
dev_dbg(gg->dev, "%s: buff %p, len %d\n", __func__, buff, length);
req = aic_ep_alloc_request(&ep->ep, GFP_ATOMIC);
gg->ep0_reply = req;
if (!req) {
dev_warn(gg->dev, "%s: cannot alloc req\n", __func__);
return -ENOMEM;
}
req->buf = gg->ep0_buff;
req->length = length;
/*
* zero flag is for sending zlp in DATA IN stage. It has no impact on
* STATUS stage.
*/
req->zero = 0;
req->complete = aic_ep_complete_empty;
if (length)
memcpy(req->buf, buff, length);
ret = aic_ep_queue_request_nolock(&ep->ep, req, GFP_ATOMIC);
if (ret) {
dev_warn(gg->dev, "%s: cannot queue req\n", __func__);
return ret;
}
return 0;
}
static int aic_ep0_process_req_feature(struct aic_usb_gadget *gg,
struct usb_ctrlrequest *ctrl)
{
struct aic_usb_ep *ep0 = gg->eps_out[0];
struct aic_usb_req *a_req;
bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
struct aic_usb_ep *ep;
int ret;
bool halted;
u32 recip;
u32 wValue;
u32 wIndex;
dev_dbg(gg->dev, "%s: %s_FEATURE\n",
__func__, set ? "SET" : "CLEAR");
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
if (set)
gg->remote_wakeup_allowed = 1;
else
gg->remote_wakeup_allowed = 0;
break;
case USB_DEVICE_TEST_MODE:
if ((wIndex & 0xff) != 0)
return -EINVAL;
if (!set)
return -EINVAL;
gg->test_mode = wIndex >> 8;
break;
default:
return -ENOENT;
}
ret = aic_ep0_enqueue_reply(gg, ep0, NULL, 0);
if (ret) {
dev_err(gg->dev,
"%s: failed to send reply\n", __func__);
return ret;
}
break;
case USB_RECIP_ENDPOINT:
ep = windex_to_ep(gg, wIndex);
if (!ep) {
dev_dbg(gg->dev, "%s: no endpoint for 0x%04x\n",
__func__, wIndex);
return -ENOENT;
}
switch (wValue) {
case USB_ENDPOINT_HALT:
halted = ep->halted;
aic_ep_sethalt_nolock(&ep->ep, set, true);
ret = aic_ep0_enqueue_reply(gg, ep0, NULL, 0);
if (ret) {
dev_err(gg->dev,
"%s: failed to send reply\n", __func__);
return ret;
}
if (!set && halted) {
if (ep->req) {
a_req = ep->req;
ep->req = NULL;
list_del_init(&a_req->queue);
if (a_req->req.complete) {
spin_unlock(&gg->lock);
usb_gadget_giveback_request
(&ep->ep, &a_req->req);
spin_lock(&gg->lock);
}
}
/* If we have pending request, then start it */
if (!ep->req)
aic_ep_start_next_request(ep);
}
break;
default:
return -ENOENT;
}
break;
default:
return -ENOENT;
}
return 1;
}
static int aic_ep0_process_get_status(struct aic_usb_gadget *gg,
struct usb_ctrlrequest *ctrl)
{
struct aic_usb_ep *ep0 = gg->eps_out[0];
struct aic_usb_ep *ep;
__le16 reply;
u16 status;
int ret;
dev_dbg(gg->dev, "%s: USB_REQ_GET_STATUS\n", __func__);
if (!ep0->dir_in) {
dev_warn(gg->dev, "%s: direction out?\n", __func__);
return -EINVAL;
}
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
status = 1 << USB_DEVICE_SELF_POWERED;
status |= gg->remote_wakeup_allowed <<
USB_DEVICE_REMOTE_WAKEUP;
reply = cpu_to_le16(status);
break;
case USB_RECIP_INTERFACE:
/* currently, the data result should be zero */
reply = cpu_to_le16(0);
break;
case USB_RECIP_ENDPOINT:
ep = windex_to_ep(gg, le16_to_cpu(ctrl->wIndex));
if (!ep)
return -ENOENT;
reply = cpu_to_le16(ep->halted ? 1 : 0);
break;
default:
return 0;
}
if (le16_to_cpu(ctrl->wLength) != 2)
return -EINVAL;
ret = aic_ep0_enqueue_reply(gg, ep0, &reply, 2);
if (ret) {
dev_err(gg->dev, "%s: failed to send reply\n", __func__);
return ret;
}
return 1;
}
static void aic_ep0_process_control(struct aic_usb_gadget *gg,
struct usb_ctrlrequest *ctrl)
{
struct aic_usb_ep *ep0 = gg->eps_out[0];
u32 reg = 0;
int ret = 0;
dev_dbg(gg->dev,
"ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n",
ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
ctrl->wIndex, ctrl->wLength);
if (ctrl->wLength == 0) {
ep0->dir_in = 1;
gg->ep0_state = AIC_EP0_STATUS_IN;
} else if (ctrl->bRequestType & USB_DIR_IN) {
ep0->dir_in = 1;
gg->ep0_state = AIC_EP0_DATA_IN;
} else {
ep0->dir_in = 0;
gg->ep0_state = AIC_EP0_DATA_OUT;
}
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
gg->connected = 1;
reg = aic_readl(gg, USBDEVCONF);
reg &= ~USBDEVCONF_DEVADDR_MASK;
reg |= (le16_to_cpu(ctrl->wValue) <<
USBDEVCONF_DEVADDR_SHIFT) &
USBDEVCONF_DEVADDR_MASK;
aic_writel(gg, reg, USBDEVCONF);
dev_info(gg->dev, "new address %d\n", ctrl->wValue);
ret = aic_ep0_enqueue_reply(gg, ep0, NULL, 0);
return;
case USB_REQ_GET_STATUS:
ret = aic_ep0_process_get_status(gg, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
ret = aic_ep0_process_req_feature(gg, ctrl);
break;
}
}
/* driver's setup() callback */
if (ret == 0 && gg->driver) {
spin_unlock(&gg->lock);
ret = gg->driver->setup(&gg->gadget, ctrl);
spin_lock(&gg->lock);
if (ret < 0)
dev_dbg(gg->dev, "driver->setup() ret %d\n", ret);
}
gg->delayed_status = false;
if (ret == USB_GADGET_DELAYED_STATUS)
gg->delayed_status = true;
if (ret < 0)
aic_stall_ep0(gg);
}
static void aic_ep0_complete_setup(struct usb_ep *ep,
struct usb_request *req)
{
struct aic_usb_ep *hs_ep = our_ep(ep);
struct aic_usb_gadget *gg = hs_ep->parent;
if (req->status < 0) {
dev_dbg(gg->dev, "%s: failed %d\n", __func__, req->status);
return;
}
spin_lock(&gg->lock);
if (req->actual == 0)
aic_ep0_enqueue_setup(gg);
else
aic_ep0_process_control(gg, req->buf);
spin_unlock(&gg->lock);
}
static void aic_ep0_enqueue_setup(struct aic_usb_gadget *gg)
{
struct usb_request *req = gg->ctrl_req;
struct aic_usb_req *a_req = our_req(req);
int ret;
dev_dbg(gg->dev, "%s: queueing setup request\n", __func__);
req->zero = 0;
req->length = 8;
req->buf = gg->ctrl_buff;
req->complete = aic_ep0_complete_setup;
if (!list_empty(&a_req->queue)) {
dev_dbg(gg->dev, "%s already queued???\n", __func__);
return;
}
gg->eps_out[0]->dir_in = 0;
gg->eps_out[0]->send_zlp = 0;
gg->ep0_state = AIC_EP0_SETUP;
ret = aic_ep_queue_request_nolock(&gg->eps_out[0]->ep, req, GFP_ATOMIC);
if (ret < 0)
dev_err(gg->dev, "%s: failed queue (%d)\n", __func__, ret);
}
static void aic_ep_program_zlp(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep)
{
u32 ctrl;
u8 index = a_ep->index;
u32 ctl_addr = a_ep->dir_in ? INEPCFG(index) : OUTEPCFG(index);
u32 siz_addr = a_ep->dir_in ? INEPTSFSIZ(index) : OUTEPTSFSIZ(index);
if (a_ep->dir_in)
dev_dbg(gg->dev, "Sending zero-length packet on ep%d\n",
index);
else
dev_dbg(gg->dev, "Receiving zero-length packet on ep%d\n",
index);
aic_writel(gg, EPTSIZ_MC(1) | EPTSIZ_PKTCNT(1) |
EPTSIZ_XFERSIZE(0),
siz_addr);
ctrl = aic_readl(gg, ctl_addr);
ctrl |= EPCTL_CNAK; /* clear NAK set by core */
ctrl |= EPCTL_EPENA; /* ensure ep enabled */
ctrl |= EPCTL_USBACTEP;
aic_writel(gg, ctrl, ctl_addr);
}
static void aic_ep0_program_zlp(struct aic_usb_gadget *gg, bool dir_in)
{
/* eps_out[0] is used in both directions */
gg->eps_out[0]->dir_in = dir_in;
gg->ep0_state = dir_in ? AIC_EP0_STATUS_IN : AIC_EP0_STATUS_OUT;
aic_ep_program_zlp(gg, gg->eps_out[0]);
}
static void aic_epint_handle_nak(struct aic_usb_ep *a_ep)
{
struct aic_usb_gadget *gg = a_ep->parent;
int dir_in = a_ep->dir_in;
if (!dir_in || !a_ep->isochronous)
return;
if (a_ep->target_frame == TARGET_FRAME_INITIAL) {
a_ep->target_frame = gg->frame_number;
if (a_ep->interval > 1) {
u32 ctrl = aic_readl(gg,
INEPCFG(a_ep->index));
if (a_ep->target_frame & 0x1)
ctrl |= EPCTL_SETODDFR;
else
ctrl |= EPCTL_SETEVENFR;
aic_writel(gg, ctrl, INEPCFG(a_ep->index));
}
aic_ep_complete_request(gg, a_ep,
get_ep_head(a_ep), 0);
}
aic_incr_frame_num(a_ep);
}
static void aic_epint_handle_outtoken_ep_disabled(struct aic_usb_ep *ep)
{
struct aic_usb_gadget *gg = ep->parent;
int dir_in = ep->dir_in;
u32 doepmsk;
if (dir_in || !ep->isochronous)
return;
if (ep->interval > 1 &&
ep->target_frame == TARGET_FRAME_INITIAL) {
u32 ctrl;
ep->target_frame = gg->frame_number;
aic_incr_frame_num(ep);
ctrl = aic_readl(gg, OUTEPCFG(ep->index));
if (ep->target_frame & 0x1)
ctrl |= EPCTL_SETODDFR;
else
ctrl |= EPCTL_SETEVENFR;
aic_writel(gg, ctrl, OUTEPCFG(ep->index));
}
aic_ep_start_next_request(ep);
doepmsk = aic_readl(gg, OUTEPINTMSK);
doepmsk &= ~OUTEPINTMSK_OUTTKNEPDISMSK;
aic_writel(gg, doepmsk, OUTEPINTMSK);
}
static void aic_epint_handle_ep_disabled(struct aic_usb_ep *a_ep)
{
struct aic_usb_gadget *gg = a_ep->parent;
struct aic_usb_req *a_req;
unsigned char idx = a_ep->index;
int dir_in = a_ep->dir_in;
u32 epctl_addr = dir_in ? INEPCFG(idx) : OUTEPCFG(idx);
int dctl = aic_readl(gg, USBDEVFUNC);
dev_dbg(gg->dev, "%s: EPDisbld\n", __func__);
if (dir_in) {
int epctl = aic_readl(gg, epctl_addr);
aic_flush_tx_fifo(gg, a_ep->fifo_index);
if (a_ep->isochronous) {
aic_epint_handle_complete_in(gg, a_ep);
return;
}
if ((epctl & EPCTL_STALL) && (epctl & EPCTL_EPTYPE_BULK)) {
int dctl = aic_readl(gg, USBDEVFUNC);
dctl |= USBDEVFUNC_CGNPINNAK;
aic_writel(gg, dctl, USBDEVFUNC);
}
return;
}
if (dctl & USBDEVFUNC_GOUTNAKSTS) {
dctl |= USBDEVFUNC_CGOUTNAK;
aic_writel(gg, dctl, USBDEVFUNC);
}
if (!a_ep->isochronous)
return;
if (list_empty(&a_ep->queue)) {
dev_dbg(gg->dev, "%s: complete_ep 0x%p, ep->queue empty!\n",
__func__, a_ep);
return;
}
do {
a_req = get_ep_head(a_ep);
if (a_req)
aic_ep_complete_request(gg, a_ep, a_req,
-ENODATA);
aic_incr_frame_num(a_ep);
/* Update current frame number value. */
gg->frame_number = aic_read_frameno(gg);
} while (aic_target_frame_elapsed(a_ep));
aic_ep_start_next_request(a_ep);
}
static void aic_epint_handle_complete_in(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep)
{
struct aic_usb_req *a_req = a_ep->req;
u32 epsize = aic_readl(gg, INEPTSFSIZ(a_ep->index));
int size_left, size_done;
if (!a_req) {
dev_dbg(gg->dev, "XferCompl but no req\n");
return;
}
/* Finish ZLP handling for IN EP0 transactions */
if (a_ep->index == 0 && gg->ep0_state == AIC_EP0_STATUS_IN) {
dev_dbg(gg->dev, "zlp packet sent\n");
/*
* While send zlp for DWC2_EP0_STATUS_IN EP direction was
* changed to IN. Change back to complete OUT transfer request
*/
a_ep->dir_in = 0;
aic_ep_complete_request(gg, a_ep, a_req, 0);
if (gg->test_mode) {
int ret;
ret = aic_set_test_mode(gg, gg->test_mode);
if (ret < 0) {
dev_dbg(gg->dev, "Invalid Test #%d\n",
gg->test_mode);
aic_stall_ep0(gg);
return;
}
}
aic_ep0_enqueue_setup(gg);
return;
}
/* calculate xfer length */
size_left = EPTSIZ_XFERSIZE_GET(epsize);
size_done = a_ep->size_loaded - size_left;
size_done += a_ep->last_load;
/* update req.actual */
if (a_req->req.actual != size_done)
dev_dbg(gg->dev, "%s: adjusting size done %d => %d\n",
__func__, a_req->req.actual, size_done);
a_req->req.actual = size_done;
dev_dbg(gg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
a_req->req.length, a_req->req.actual, a_req->req.zero);
/* request remain data, continue xfer */
if (!size_left && a_req->req.actual < a_req->req.length) {
dev_dbg(gg->dev, "%s trying more for req...\n", __func__);
aic_ep_start_req(gg, a_ep, a_req, true);
return;
}
/* Zlp for all endpoints, for ep0 only in DATA IN stage */
if (a_ep->send_zlp) {
aic_ep_program_zlp(gg, a_ep);
a_ep->send_zlp = 0;
/* transfer will be completed on next complete interrupt */
return;
}
if (a_ep->index == 0 && gg->ep0_state == AIC_EP0_DATA_IN) {
/* Move to STATUS OUT */
aic_ep0_program_zlp(gg, false);
return;
}
aic_ep_complete_request(gg, a_ep, a_req, 0);
}
static void aic_epint_handle_outdone(struct aic_usb_gadget *gg, int epnum)
{
u32 epsize = aic_readl(gg, OUTEPTSFSIZ(epnum));
struct aic_usb_ep *a_ep = gg->eps_out[epnum];
struct aic_usb_req *a_req = a_ep->req;
struct usb_request *req = &a_req->req;
unsigned int size_left = EPTSIZ_XFERSIZE_GET(epsize);
unsigned int size_done = 0;
int ret = 0;
if (!a_req) {
dev_dbg(gg->dev, "%s: no request active\n", __func__);
return;
}
if (epnum == 0 && gg->ep0_state == AIC_EP0_STATUS_OUT) {
dev_dbg(gg->dev, "zlp packet received\n");
aic_ep_complete_request(gg, a_ep, a_req, 0);
aic_ep0_enqueue_setup(gg);
return;
}
size_done = a_ep->size_loaded - size_left;
size_done += a_ep->last_load;
req->actual = size_done;
/* request remain data, continue xfer */
if (req->actual < req->length && size_left == 0) {
aic_ep_start_req(gg, a_ep, a_req, true);
return;
}
if (req->actual < req->length && req->short_not_ok) {
dev_dbg(gg->dev, "%s: got %d/%d (short not ok) => error\n",
__func__, req->actual, req->length);
/*
* todo - what should we return here? there's no one else
* even bothering to check the status.
*/
}
if (epnum == 0 && gg->ep0_state == AIC_EP0_DATA_OUT) {
/* Move to STATUS IN */
if (!gg->delayed_status) {
aic_ep0_program_zlp(gg, true);
ret = -EISCONN;
}
}
/* Set actual frame number for completed transfers */
if (a_ep->isochronous)
req->frame_number = gg->frame_number;
aic_ep_complete_request(gg, a_ep, a_req, ret);
}
/* func: aic_inep0_open
* gg->eps_in/out[0]->ep.desc(ep0) dose not have usb_endpoint_descriptor,
* using aic_ep_enable will cause NULL point.
* So use this func to enable ep0 IN_EP0_CFG.
*/
static void aic_inep0_open(struct aic_usb_gadget *gg)
{
u32 reg = 0;
unsigned int mps;
/* mark tx_fifo_map */
gg->tx_fifo_map |= (1 << 0);
mps = aic_ep0_mps(gg->eps_in[0]->ep.maxpacket);
reg = aic_readl(gg, INEPCFG0);
reg |= mps | EPCTL_SETD0PID | EPCTL_USBACTEP;
aic_writel(gg, reg, INEPCFG0);
/* Enable interrupts for EP0 in */
aic_ctrl_epint(gg, 0, 1, 1);
}
static int aic_npinep_rewrite(struct aic_usb_gadget *gg, unsigned int idx)
{
unsigned int pending_map = 0;
int i = 0, j = 0, fail = 0;
u32 reg = 0;
for (i = 0U, j = 0U; i < gg->params.num_ep; i++) {
reg = aic_readl(gg, INEPCFG(i));
if (!((gg->tx_fifo_map & (1 << i)) && (reg & EPCTL_EPENA)))
continue;
/* mark the fifo which need to be rewrite */
j++;
pending_map |= (1 << i);
}
/* Mismatch must be caused by two or more ep */
if (j <= 1)
return 0;
dev_dbg(gg->dev, "mismatch ep pending_map:0x%x\n", pending_map);
/* (1) close all no-periodic ep */
/* (1.1) Set Global In NP NAK in Shared FIFO for non periodic ep */
reg = aic_readl(gg, USBINTSTS);
if (!(reg & USBINTSTS_GINNAKEFF)) {
reg = aic_readl(gg, USBDEVFUNC);
reg |= USBDEVFUNC_SGNPINNAK;
aic_writel(gg, reg, USBDEVFUNC);
} else {
dev_err(gg->dev,
"%s GNINAKEFF is not 0, reg:0x%x, check bit:0x%x\n",
__func__, reg, (unsigned int)(reg & USBINTSTS_GINNAKEFF));
}
for (i = 0; i < DIS_EP_TIMOUT; i++) {
reg = aic_readl(gg, USBINTSTS);
/* wait for SGNPINNAK to take effect, this bit will be set to 1 */
if (reg & USBINTSTS_GINNAKEFF)
break;
udelay(1);
}
dev_dbg(gg->dev, "USBINTSTS(0x%x) val:0x%x\n", (unsigned int)USBINTSTS, reg);
if (i == DIS_EP_TIMOUT)
dev_err(gg->dev, "%s timeout USBINTSTS.GOUTNAKEFF\n", __func__);
/* (1.2) Disable ep */
for (i = 0U; i < gg->params.num_ep; i++) {
reg = aic_readl(gg, INEPCFG(i));
if (!((gg->tx_fifo_map & (1 << i)) && (reg & EPCTL_EPENA)))
continue;
/* set NACK and disabled ep */
reg |= EPCTL_SNAK;
reg |= EPCTL_EPDIS;
aic_writel(gg, reg, INEPCFG(i));
}
/* check if ep disabled complete */
for (j = 0; j < DIS_EP_TIMOUT; j++) {
fail = 0;
for (i = 0U; i < gg->params.num_ep; i++) {
if (!(pending_map & (1 << i)))
continue;
/* wait for ep disabled finish */
reg = aic_readl(gg, INEPINT(i));
if (!(reg & EPINT_EPDISBLD)) {
fail = i + 1;
break;
}
}
if (!fail)
break;
udelay(1);
}
if (j == DIS_EP_TIMOUT)
dev_err(gg->dev,
"%s ep%d timeout OUTEPCFG.EPDisable\n", __func__, fail - 1);
/* Clear EPDISBLD interrupt */
for (i = 0U; i < gg->params.num_ep; i++) {
if (!(pending_map & (1 << i)))
continue;
reg = aic_readl(gg, INEPINT(i));
reg |= EPINT_EPDISBLD;
aic_writel(gg, reg, INEPINT(i));
}
/* (1.3) Flush TX FIFO0 */
aic_flush_tx_fifo(gg, 0);
/* (1.4) Clear Global In NP NAK in Shared FIFO for non periodic ep */
reg = aic_readl(gg, USBDEVFUNC);
reg |= USBDEVFUNC_CGNPINNAK;
aic_writel(gg, reg, USBDEVFUNC);
/* (2) reopen current ep */
if (idx == 0)
aic_inep0_open(gg);
else
aic_ep_enable(&gg->eps_in[idx]->ep, gg->eps_in[idx]->ep.desc);
/* (3) rewrite current ep */
dev_dbg(gg->dev, "start req: ep%d, req:%p\n", idx, gg->eps_in[idx]->req);
if (pending_map & (1 << idx))
aic_ep_start_req(gg, gg->eps_in[idx], gg->eps_in[idx]->req, false);
/* (4) reopen & rewrite other ep, let's receive ep mismtach interrupt */
for (i = 0U; i < gg->params.num_ep; i++) {
if (!(pending_map & (1 << i)) || i == idx)
continue;
dev_dbg(gg->dev, "deal with another ep%d\n", i);
if (i == 0)
aic_inep0_open(gg);
else
aic_ep_enable(&gg->eps_in[i]->ep, gg->eps_in[i]->ep.desc);
aic_ep_start_req(gg, gg->eps_in[i], gg->eps_in[i]->req, true);
}
return 0;
}
static void aic_epint_irq(struct aic_usb_gadget *gg, unsigned int idx,
int dir_in)
{
struct aic_usb_ep *a_ep = index_to_ep(gg, idx, dir_in);
u32 int_addr = dir_in ? INEPINT(idx) : OUTEPINT(idx);
u32 ctl_addr = dir_in ? INEPCFG(idx) : OUTEPCFG(idx);
u32 siz_addr = dir_in ? INEPTSFSIZ(idx) : OUTEPTSFSIZ(idx);
u32 ints;
u32 ctrl;
if (!a_ep) {
dev_err(gg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
__func__, idx, dir_in ? "in" : "out");
return;
}
/* read interrupt status */
ints = aic_read_ep_interrupts(gg, idx, dir_in);
ctrl = aic_readl(gg, ctl_addr);
dev_dbg(gg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
__func__, idx, dir_in ? "in" : "out", ints);
/* clear interrupt status */
aic_writel(gg, ints, int_addr);
/* Don't process XferCompl interrupt if it is a setup packet */
if (idx == 0 && (ints & (EPINT_SETUP | EPINT_SETUP_RCVD)))
ints &= ~EPINT_XFERCOMPL;
if (ints & EPINT_XFERCOMPL) {
dev_dbg(gg->dev,
"%s: XferCompl: DxEPCTL=0x%08x, EPTSIZ=%08x\n",
__func__, aic_readl(gg, ctl_addr),
aic_readl(gg, siz_addr));
if (dir_in) {
/*
* We get OutDone from the FIFO, so we only
* need to look at completing IN requests here
* if operating slave mode
*/
if (a_ep->isochronous && a_ep->interval > 1)
aic_incr_frame_num(a_ep);
aic_epint_handle_complete_in(gg, a_ep);
if (ints & EPINT_NAKINTRPT)
ints &= ~EPINT_NAKINTRPT;
if (idx == 0 && !a_ep->req)
aic_ep0_enqueue_setup(gg);
} else {
/*
* We're using DMA, we need to fire an OutDone here
* as we ignore the RXFIFO.
*/
if (a_ep->isochronous && a_ep->interval > 1)
aic_incr_frame_num(a_ep);
aic_epint_handle_outdone(gg, idx);
}
}
if (ints & EPINT_EPDISBLD)
aic_epint_handle_ep_disabled(a_ep);
if (ints & EPINT_OUTTKNEPDIS)
aic_epint_handle_outtoken_ep_disabled(a_ep);
if (ints & EPINT_NAKINTRPT)
aic_epint_handle_nak(a_ep);
if (ints & EPINT_AHBERR)
dev_dbg(gg->dev, "%s: AHBErr\n", __func__);
if (ints & EPINT_SETUP) { /* Setup or Timeout */
dev_dbg(gg->dev, "%s: Setup/Timeout\n", __func__);
if (idx == 0) {
/*
* this is the notification we've received a
* setup packet. In non-DMA mode we'd get this
* from the RXFIFO, instead we need to process
* the setup here.
*/
if (dir_in)
WARN_ON_ONCE(1);
else
aic_epint_handle_outdone(gg, 0);
}
}
if (ints & EPINT_STSPHSERCVD)
dev_dbg(gg->dev, "%s: StsPhseRcvd\n", __func__);
if (ints & EPINT_BACK2BACKSETUP)
dev_dbg(gg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
if (dir_in && !a_ep->isochronous) {
/* not sure if this is important, but we'll clear it anyway */
if (ints & EPINT_INTKNTXFEMP) {
dev_dbg(gg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
__func__, idx);
}
/* this probably means something bad is happening */
if (ints & EPINT_INTKNEPMIS) {
dev_warn(gg->dev, "%s: ep%d: INTknEP\n",
__func__, idx);
aic_npinep_rewrite(gg, idx);
}
}
}
static void aic_irq_handle_epint(struct aic_usb_gadget *gg)
{
u32 int_val = aic_readl(gg, USBEPINT);
u32 mask = aic_readl(gg, USBEPINTMSK);
u32 int_out, int_in;
int i;
int_val &= mask;
int_out = int_val >> USBEPINT_OUTEP_SHIFT;
int_in = int_val & 0xFFFF;
dev_dbg(gg->dev, "%s: int=%08x\n", __func__, int_val);
for (i = 0; i < gg->params.num_ep && int_out;
i++, int_out >>= 1) {
if (int_out & 1)
aic_epint_irq(gg, i, 0);
}
for (i = 0; i < gg->params.num_ep && int_in;
i++, int_in >>= 1) {
if (int_in & 1)
aic_epint_irq(gg, i, 1);
}
}
static void aic_irq_handle_enumdone(struct aic_usb_gadget *gg)
{
u32 reg = 0;
int ep0_mps = 0;
int ep_mps = 8;
/* clear interrupt status */
aic_writel(gg, USBINTSTS_ENUMDONE, USBINTSTS);
/* read enumerated speed */
reg = aic_readl(gg, USBLINESTS);
dev_dbg(gg->dev, "EnumDone (USBLINESTS=0x%08x)\n", reg);
switch ((reg & USBLINESTS_ENUMSPD_MASK) >> USBLINESTS_ENUMSPD_SHIFT) {
case USBLINESTS_ENUMSPD_FS:
case USBLINESTS_ENUMSPD_FS48:
gg->gadget.speed = USB_SPEED_FULL;
ep0_mps = EP0_MPS_LIMIT;
ep_mps = 1023;
break;
case USBLINESTS_ENUMSPD_HS:
gg->gadget.speed = USB_SPEED_HIGH;
ep0_mps = EP0_MPS_LIMIT;
ep_mps = 1024;
break;
case USBLINESTS_ENUMSPD_LS:
gg->gadget.speed = USB_SPEED_LOW;
ep0_mps = 8;
ep_mps = 8;
break;
}
dev_info(gg->dev, "new device is %s\n",
usb_speed_string(gg->gadget.speed));
/* update endpoint maxpacket */
if (ep0_mps) {
int i;
/* Initialize ep0 for both in and out directions */
aic_set_ep_maxpacket(gg, 0, ep0_mps, 0, 1);
aic_set_ep_maxpacket(gg, 0, ep0_mps, 0, 0);
for (i = 1; i < gg->params.num_ep; i++) {
if (gg->eps_in[i])
aic_set_ep_maxpacket(gg, i, ep_mps,
0, 1);
if (gg->eps_out[i])
aic_set_ep_maxpacket(gg, i, ep_mps,
0, 0);
}
}
/* ep0 request */
aic_ep0_enqueue_setup(gg);
dev_dbg(gg->dev, "EP0: INEPCFG0=0x%08x, OUTEPCFG0=0x%08x\n",
aic_readl(gg, INEPCFG0),
aic_readl(gg, OUTEPCFG0));
}
static void aic_irq_handle_usbrst(struct aic_usb_gadget *gg)
{
/* clear interrupt status */
aic_writel(gg, USBINTSTS_USBRST, USBINTSTS);
dev_dbg(gg->dev, "%s: USBRst\n", __func__);
dev_dbg(gg->dev, "NPTXFIFOSTS=%08x\n",
aic_readl(gg, NPTXFIFOSTS));
/* Reset device address to zero */
aic_clear_bit(gg, USBDEVCONF, USBDEVCONF_DEVADDR_MASK);
if (gg->connected) {
/* cleanup endpoints request */
aic_core_disconnect(gg);
/* reinit hardware core register */
aic_core_init(gg, true);
}
}
static void aic_irq_handle_goutnak(struct aic_usb_gadget *gg)
{
u8 idx;
u32 epctrl;
u32 gintmsk;
u32 daintmsk;
struct aic_usb_ep *a_ep;
daintmsk = aic_readl(gg, USBEPINTMSK);
daintmsk >>= USBEPINT_OUTEP_SHIFT;
/* Mask this interrupt */
gintmsk = aic_readl(gg, USBINTMSK);
gintmsk &= ~USBINTSTS_GOUTNAKEFF;
aic_writel(gg, gintmsk, USBINTMSK);
dev_dbg(gg->dev, "GOUTNakEff triggered\n");
for (idx = 1; idx < gg->params.num_ep; idx++) {
a_ep = gg->eps_out[idx];
/* Proceed only unmasked ISOC EPs */
if ((BIT(idx) & ~daintmsk) || !a_ep->isochronous)
continue;
epctrl = aic_readl(gg, OUTEPCFG(idx));
if (epctrl & EPCTL_EPENA) {
epctrl |= EPCTL_SNAK;
epctrl |= EPCTL_EPDIS;
aic_writel(gg, epctrl, OUTEPCFG(idx));
}
}
}
static void aic_irq_handle_incomplete_isoc_in(struct aic_usb_gadget *gg)
{
struct aic_usb_ep *a_ep;
u32 epctrl;
u32 daintmsk;
u32 idx;
dev_dbg(gg->dev, "Incomplete isoc in interrupt received:\n");
/* Clear interrupt */
aic_writel(gg, USBINTSTS_INCOMPL_SOIN, USBINTSTS);
daintmsk = aic_readl(gg, USBEPINTMSK);
for (idx = 1; idx < gg->params.num_ep; idx++) {
a_ep = gg->eps_in[idx];
/* Proceed only unmasked ISOC EPs */
if ((BIT(idx) & ~daintmsk) || !a_ep->isochronous)
continue;
epctrl = aic_readl(gg, INEPCFG(idx));
if ((epctrl & EPCTL_EPENA) &&
aic_target_frame_elapsed(a_ep)) {
epctrl |= EPCTL_SNAK;
epctrl |= EPCTL_EPDIS;
aic_writel(gg, epctrl, INEPCFG(idx));
}
}
}
static void aic_irq_handle_incomplete_isoc_out(struct aic_usb_gadget *gg)
{
u32 gintsts;
u32 gintmsk;
u32 daintmsk;
u32 epctrl;
struct aic_usb_ep *a_ep;
int idx;
dev_dbg(gg->dev, "%s: USBINTSTS_INCOMPL_SOOUT\n", __func__);
/* Clear interrupt */
aic_writel(gg, USBINTSTS_INCOMPL_SOOUT, USBINTSTS);
daintmsk = aic_readl(gg, USBEPINTMSK);
daintmsk >>= USBEPINT_OUTEP_SHIFT;
for (idx = 1; idx < gg->params.num_ep; idx++) {
a_ep = gg->eps_out[idx];
/* Proceed only unmasked ISOC EPs */
if ((BIT(idx) & ~daintmsk) || !a_ep->isochronous)
continue;
epctrl = aic_readl(gg, OUTEPCFG(idx));
if ((epctrl & EPCTL_EPENA) &&
aic_target_frame_elapsed(a_ep)) {
/* Unmask GOUTNAKEFF interrupt */
gintmsk = aic_readl(gg, USBINTMSK);
gintmsk |= USBINTSTS_GOUTNAKEFF;
aic_writel(gg, gintmsk, USBINTMSK);
gintsts = aic_readl(gg, USBINTSTS);
if (!(gintsts & USBINTSTS_GOUTNAKEFF)) {
aic_set_bit(gg, USBDEVFUNC,
USBDEVFUNC_SGOUTNAK);
break;
}
}
}
}
static irqreturn_t aic_udc_irq(int irq, void *data)
{
struct aic_usb_gadget *gg = data;
u32 intsts = 0;
u32 intmsk = 0;
int retry = 8;
spin_lock(&gg->lock);
intsts = aic_readl(gg, USBINTSTS);
intmsk = aic_readl(gg, USBINTMSK);
dev_dbg(gg->dev, "%s: %08x %08x (%08x) retry %d\n",
__func__, intsts, intsts & intmsk, intmsk, retry);
if (intsts & USBINTSTS_USBRST)
aic_irq_handle_usbrst(gg);
if (intsts & USBINTSTS_ENUMDONE)
aic_irq_handle_enumdone(gg);
if (intsts & (USBINTSTS_OEPINT | USBINTSTS_IEPINT))
aic_irq_handle_epint(gg);
if (intsts & USBINTSTS_NPTXFEMP) {
dev_dbg(gg->dev, "NPTxFEmp\n");
aic_dis_gsint(gg, USBINTSTS_NPTXFEMP);
}
if (intsts & USBINTSTS_RXFLVL)
dev_dbg(gg->dev, "RxFLVL\n");
if (intsts & USBINTSTS_ERLYSUSP) {
dev_dbg(gg->dev, "ErlySusp\n");
aic_writel(gg, USBINTSTS_ERLYSUSP, USBINTSTS);
}
if (intsts & USBINTSTS_GOUTNAKEFF)
aic_irq_handle_goutnak(gg);
if (intsts & USBINTSTS_GINNAKEFF) {
dev_info(gg->dev, "GINNakEff triggered\n");
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_CGNPINNAK);
}
if (intsts & USBINTSTS_INCOMPL_SOIN)
aic_irq_handle_incomplete_isoc_in(gg);
if (intsts & USBINTSTS_INCOMPL_SOOUT)
aic_irq_handle_incomplete_isoc_out(gg);
if (intsts & USBINTSTS_WKUPINT) {
dev_dbg(gg->dev, "WKUP\n");
aic_writel(gg, USBINTSTS_WKUPINT, USBINTSTS);
}
if (intsts & USBINTSTS_USBSUSP) {
dev_dbg(gg->dev, "USB SUSPEND\n");
aic_writel(gg, USBINTSTS_USBSUSP, USBINTSTS);
}
spin_unlock(&gg->lock);
return IRQ_HANDLED;
}
static int aic_handle_unaligned_req(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep,
struct aic_usb_req *a_req)
{
void *req_buf = a_req->req.buf;
/* If buffer is aligned */
if ((!((long)req_buf % L1_CACHE_BYTES) &&
!(a_req->req.length % L1_CACHE_BYTES))
|| (a_ep->index == 0))
return 0;
WARN_ON(a_req->saved_req_buf);
dev_dbg(gg->dev, "%s: %s: buf=%p length=%d\n", __func__,
a_ep->ep.name, req_buf, a_req->req.length);
a_req->req.buf = kmalloc(ALIGN(a_req->req.length, L1_CACHE_BYTES),
GFP_ATOMIC);
if (!a_req->req.buf) {
a_req->req.buf = req_buf;
dev_err(gg->dev,
"%s: unable to allocate memory for bounce buffer\n",
__func__);
return -ENOMEM;
}
/* Save actual buffer */
a_req->saved_req_buf = req_buf;
if (a_ep->dir_in)
memcpy(a_req->req.buf, req_buf, a_req->req.length);
return 0;
}
static void
aic_handle_unaligned_req_complete(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep,
struct aic_usb_req *a_req)
{
/* If buffer was aligned */
if (!a_req->saved_req_buf)
return;
dev_dbg(gg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
a_ep->ep.name, a_req->req.status, a_req->req.actual);
/* Copy data from bounce buffer on successful out transfer */
if (!a_ep->dir_in && !a_req->req.status)
memcpy(a_req->saved_req_buf, a_req->req.buf,
a_req->req.actual);
/* Free bounce buffer */
kfree(a_req->req.buf);
a_req->req.buf = a_req->saved_req_buf;
a_req->saved_req_buf = NULL;
}
static void aic_ep_stop_xfer(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep)
{
u32 ctrl_addr;
u32 int_addr;
ctrl_addr = a_ep->dir_in ? INEPCFG(a_ep->index) :
OUTEPCFG(a_ep->index);
int_addr = a_ep->dir_in ? INEPINT(a_ep->index) :
OUTEPINT(a_ep->index);
dev_dbg(gg->dev, "%s: stopping transfer on %s\n", __func__,
a_ep->name);
if (a_ep->dir_in) {
if (a_ep->periodic) {
aic_set_bit(gg, ctrl_addr, EPCTL_SNAK);
/* Wait for Nak effect */
if (aic_wait_bit_set(gg, int_addr,
EPINT_INEPNAKEFF, 100))
dev_warn(gg->dev,
"%s: timeout INEPINT.NAKEFF\n",
__func__);
} else {
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_SGNPINNAK);
/* Wait for Nak effect */
if (aic_wait_bit_set(gg, USBINTSTS,
USBINTSTS_GINNAKEFF, 100))
dev_warn(gg->dev,
"%s: timeout USBINTSTS.GINNAKEFF\n",
__func__);
}
} else {
if (!(aic_readl(gg, USBINTSTS) & USBINTSTS_GOUTNAKEFF))
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_SGOUTNAK);
/* Wait for global nak to take effect */
if (aic_wait_bit_set(gg, USBINTSTS,
USBINTSTS_GOUTNAKEFF, 100))
dev_warn(gg->dev, "%s: timeout USBINTSTS.GOUTNAKEFF\n",
__func__);
}
/* Disable ep */
aic_set_bit(gg, ctrl_addr, EPCTL_EPDIS | EPCTL_SNAK);
/* Wait for ep to be disabled */
if (aic_wait_bit_set(gg, int_addr, EPINT_EPDISBLD, 100))
dev_warn(gg->dev,
"%s: timeout OUTEPCFG.EPDisable\n", __func__);
/* Clear EPDISBLD interrupt */
aic_set_bit(gg, int_addr, EPINT_EPDISBLD);
if (a_ep->dir_in) {
unsigned short fifo_index;
if (a_ep->periodic)
fifo_index = a_ep->fifo_index;
else
fifo_index = 0;
/* Flush TX FIFO */
aic_flush_tx_fifo(gg, fifo_index);
/* Clear Global In NP NAK in Shared FIFO for non periodic ep */
if (!a_ep->periodic)
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_CGNPINNAK);
} else {
/* Remove global NAKs */
aic_set_bit(gg, USBDEVFUNC, USBDEVFUNC_CGOUTNAK);
}
}
static void aic_ep_start_req(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep,
struct aic_usb_req *a_req,
bool continuing)
{
struct usb_request *ureq = &a_req->req;
int index = a_ep->index;
int dir_in = a_ep->dir_in;
u32 dma_reg;
u32 epctrl_reg;
u32 epsize_reg;
u32 epsize;
u32 ctrl;
unsigned int length;
unsigned int packets;
unsigned int maxreq;
if (index != 0) {
if (a_ep->req && !continuing) {
dev_err(gg->dev, "%s: active request\n", __func__);
WARN_ON(1);
return;
} else if (a_ep->req != a_req && continuing) {
dev_err(gg->dev,
"%s: continue different req\n", __func__);
WARN_ON(1);
return;
}
}
dma_reg = dir_in ? INEPDMAADDR(index) : OUTEPDMAADDR(index);
epctrl_reg = dir_in ? INEPCFG(index) : OUTEPCFG(index);
epsize_reg = dir_in ? INEPTSFSIZ(index) : OUTEPTSFSIZ(index);
/* stall ignore */
ctrl = aic_readl(gg, epctrl_reg);
if (index && ctrl & EPCTL_STALL) {
dev_warn(gg->dev, "%s: ep%d is stalled\n", __func__, index);
return;
}
dev_dbg(gg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n",
__func__, ctrl, index,
a_ep->dir_in ? "in" : "out");
/* length */
length = ureq->length - ureq->actual;
maxreq = get_ep_limit(a_ep);
dev_dbg(gg->dev, "ureq->length:%d ureq->actual:%d\n",
ureq->length, ureq->actual);
if (length > maxreq) {
int round = maxreq % a_ep->ep.maxpacket;
dev_dbg(gg->dev, "%s: length %d, max-req %d, r %d\n",
__func__, length, maxreq, round);
if (round)
maxreq -= round;
length = maxreq;
}
/* multi count */
if (length)
packets = DIV_ROUND_UP(length, a_ep->ep.maxpacket);
else
packets = 1; /* send one packet if length is zero. */
if (dir_in && index != 0)
if (a_ep->isochronous)
epsize = EPTSIZ_MC(packets);
else
epsize = EPTSIZ_MC(1);
else
epsize = 0;
/* zero length packet */
if (dir_in && ureq->zero && !continuing) {
/* Test if zlp is actually required. */
if ((ureq->length >= a_ep->ep.maxpacket) &&
!(ureq->length % a_ep->ep.maxpacket))
a_ep->send_zlp = 1;
}
/* update size & dma */
epsize |= EPTSIZ_PKTCNT(packets);
epsize |= EPTSIZ_XFERSIZE(length);
aic_writel(gg, epsize, epsize_reg);
dev_dbg(gg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n",
__func__, packets, length, ureq->length, epsize, epsize_reg);
if (!continuing && (length != 0)) {
aic_writel(gg, ureq->dma, dma_reg);
dev_dbg(gg->dev, "%s: %pad => 0x%08x\n",
__func__, &ureq->dma, dma_reg);
}
a_ep->req = a_req;
/* update ctrl */
if (a_ep->isochronous && a_ep->interval == 1) {
a_ep->target_frame = aic_read_frameno(gg);
aic_incr_frame_num(a_ep);
if (a_ep->target_frame & 0x1)
ctrl |= EPCTL_SETODDFR;
else
ctrl |= EPCTL_SETEVENFR;
}
dev_dbg(gg->dev, "ep0 state:%d\n", gg->ep0_state);
if (!(index == 0 && gg->ep0_state == AIC_EP0_SETUP))
ctrl |= EPCTL_CNAK; /* clear NAK set by core */
ctrl |= EPCTL_EPENA; /* ensure ep enabled */
dev_dbg(gg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
aic_writel(gg, ctrl, epctrl_reg);
/* update counter */
a_ep->size_loaded = length;
a_ep->last_load = ureq->actual;
/* check ep is enabled */
if (!(aic_readl(gg, epctrl_reg) & EPCTL_EPENA))
dev_dbg(gg->dev,
"ep%d: failed to become enabled (EPCTL=0x%08x)?\n",
index, aic_readl(gg, epctrl_reg));
dev_dbg(gg->dev, "%s: DxEPCTL=0x%08x\n",
__func__, aic_readl(gg, epctrl_reg));
/* enable ep interrupts */
aic_ctrl_epint(gg, a_ep->index, a_ep->dir_in, 1);
}
static void aic_ep_start_next_request(struct aic_usb_ep *a_ep)
{
struct aic_usb_gadget *gg = a_ep->parent;
struct aic_usb_req *a_req;
int dir_in = a_ep->dir_in;
u32 epmsk_reg = dir_in ? INEPINTMSK : OUTEPINTMSK;
u32 mask;
if (!list_empty(&a_ep->queue)) {
a_req = get_ep_head(a_ep);
aic_ep_start_req(gg, a_ep, a_req, false);
return;
}
if (!a_ep->isochronous)
return;
if (dir_in) {
dev_dbg(gg->dev, "%s: No more ISOC-IN requests\n",
__func__);
} else {
dev_dbg(gg->dev, "%s: No more ISOC-OUT requests\n",
__func__);
mask = aic_readl(gg, epmsk_reg);
mask |= OUTEPINTMSK_OUTTKNEPDISMSK;
aic_writel(gg, mask, epmsk_reg);
}
}
static void aic_ep_complete_request(struct aic_usb_gadget *gg,
struct aic_usb_ep *a_ep,
struct aic_usb_req *a_req,
int result)
{
if (!a_req) {
dev_dbg(gg->dev, "%s: nothing to complete?\n", __func__);
return;
}
dev_dbg(gg->dev, "complete: ep %p %s, req %p, %d => %p\n",
a_ep, a_ep->ep.name, a_req, result, a_req->req.complete);
if (a_req->req.status == -EINPROGRESS)
a_req->req.status = result;
/* unmap dma */
usb_gadget_unmap_request(&gg->gadget, &a_req->req, a_ep->dir_in);
/* unalign clear */
aic_handle_unaligned_req_complete(gg, a_ep, a_req);
/* dequeue */
/* When the device receives a out-data packet, device needs to send
* an empty in-status packet. Due to using aic_ep0_program_zlp()
* instead of ep_queue() which will result in gg->eps_in/out[]->req
* being cleared. To proceed with the next transfer, it is necessary
* to alloc a new req.
* When handling mismatch function, aic_npinep_rewrite() use the old
* req instead of allocing a new req. When using aic_ep0_program_zlp()
* to send a in-status packet to host, aic_ep_start_req() which was
* called by aic_ep_start_req, can not send in-status packet bacause
* req is NULL.
* So, when not alloc req to send empty packet, do not clear req.
*/
if (result != -EISCONN) {
a_ep->req = NULL;
result = 0;
}
list_del_init(&a_req->queue);
/* call complete function */
if (a_req->req.complete) {
spin_unlock(&gg->lock);
usb_gadget_giveback_request(&a_ep->ep, &a_req->req);
spin_lock(&gg->lock);
}
/* restart request */
if (!a_ep->req && result >= 0)
aic_ep_start_next_request(a_ep);
}
static void aic_stall_ep0(struct aic_usb_gadget *gg)
{
struct aic_usb_ep *ep0 = gg->eps_out[0];
u32 reg;
u32 ctrl;
dev_dbg(gg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
reg = (ep0->dir_in) ? INEPCFG0 : OUTEPCFG0;
/*
* DxEPCTL_Stall will be cleared by EP once it has
* taken effect, so no need to clear later.
*/
ctrl = aic_readl(gg, reg);
ctrl |= EPCTL_STALL;
ctrl |= EPCTL_CNAK;
aic_writel(gg, ctrl, reg);
dev_dbg(gg->dev,
"written EPCTL=0x%08x to %08x (EPCTL=0x%08x)\n",
ctrl, reg, aic_readl(gg, reg));
/* ep0 request */
aic_ep0_enqueue_setup(gg);
}
static int aic_ep_sethalt_nolock(struct usb_ep *ep, int value, bool now)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
int index = a_ep->index;
u32 epreg;
u32 epctl;
u32 xfertype;
dev_info(gg->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
if (index == 0) {
if (value)
aic_stall_ep0(gg);
else
dev_warn(gg->dev,
"%s: can't clear halt on ep0\n", __func__);
return 0;
}
if (a_ep->isochronous) {
dev_err(gg->dev, "%s is Isochronous Endpoint\n", ep->name);
return -EINVAL;
}
if (!now && value && !list_empty(&a_ep->queue)) {
dev_dbg(gg->dev, "%s request is pending, cannot halt\n",
ep->name);
return -EAGAIN;
}
if (a_ep->dir_in) {
epreg = INEPCFG(index);
epctl = aic_readl(gg, epreg);
if (value) {
epctl |= EPCTL_STALL | EPCTL_SNAK;
if (epctl & EPCTL_EPENA)
epctl |= EPCTL_EPDIS;
} else {
epctl &= ~EPCTL_STALL;
xfertype = epctl & EPCTL_EPTYPE_MASK;
if (xfertype == EPCTL_EPTYPE_BULK ||
xfertype == EPCTL_EPTYPE_INTERRUPT)
epctl |= EPCTL_SETD0PID;
}
aic_writel(gg, epctl, epreg);
} else {
epreg = OUTEPCFG(index);
epctl = aic_readl(gg, epreg);
if (value) {
epctl |= EPCTL_STALL;
} else {
epctl &= ~EPCTL_STALL;
xfertype = epctl & EPCTL_EPTYPE_MASK;
if (xfertype == EPCTL_EPTYPE_BULK ||
xfertype == EPCTL_EPTYPE_INTERRUPT)
epctl |= EPCTL_SETD0PID;
}
aic_writel(gg, epctl, epreg);
}
a_ep->halted = value;
return 0;
}
static int aic_ep_sethalt(struct usb_ep *ep, int value)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
unsigned long flags = 0;
int ret = 0;
/* Sometime ADBD set ep0 halt for unknown reason, and no release halt,
mask this operation temporary.
*/
return 0;
spin_lock_irqsave(&gg->lock, flags);
ret = aic_ep_sethalt_nolock(ep, value, false);
spin_unlock_irqrestore(&gg->lock, flags);
return ret;
}
static int aic_ep_dequeue_request(struct usb_ep *ep, struct usb_request *req)
{
struct aic_usb_req *a_req = our_req(req);
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
unsigned long flags;
dev_dbg(gg->dev, "ep_dequeue(%p,%p)\n", ep, req);
spin_lock_irqsave(&gg->lock, flags);
if (!on_list(a_ep, a_req)) {
spin_unlock_irqrestore(&gg->lock, flags);
return -EINVAL;
}
/* Dequeue already started request */
if (req == &a_ep->req->req)
aic_ep_stop_xfer(gg, a_ep);
aic_ep_complete_request(gg, a_ep, a_req, -ECONNRESET);
spin_unlock_irqrestore(&gg->lock, flags);
return 0;
}
static int aic_ep_queue_request_nolock(struct usb_ep *ep,
struct usb_request *req,
gfp_t gfp_flags)
{
struct aic_usb_req *a_req = our_req(req);
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
bool first;
int ret = 0;
/* Don't queue ISOC request if length greater than mps*mc */
if (a_ep->isochronous &&
req->length > (a_ep->mc * a_ep->ep.maxpacket)) {
dev_err(gg->dev, "req length > maxpacket*mc\n");
return -EINVAL;
}
dev_dbg(gg->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
ep->name, req, req->length, req->buf, req->no_interrupt,
req->zero, req->short_not_ok);
/* init request */
INIT_LIST_HEAD(&a_req->queue);
req->actual = 0;
req->status = -EINPROGRESS;
/* unalign address */
ret = aic_handle_unaligned_req(gg, a_ep, a_req);
if (ret)
return ret;
/* map dma */
ret = usb_gadget_map_request(&gg->gadget, req, a_ep->dir_in);
if (ret) {
dev_err(gg->dev, "%s: failed to map buffer %p, %d bytes\n",
__func__, req->buf, req->length);
return ret;
}
/* enqueue */
first = list_empty(&a_ep->queue);
list_add_tail(&a_req->queue, &a_ep->queue);
/* Change EP0 direction if status phase request is after data out */
if (!a_ep->index && !req->length && !a_ep->dir_in &&
gg->ep0_state == AIC_EP0_DATA_OUT)
a_ep->dir_in = 1;
/* start transfer */
if (first) {
if (!a_ep->isochronous) {
aic_ep_start_req(gg, a_ep, a_req, false);
return 0;
}
/* Update current frame number value. */
gg->frame_number = aic_read_frameno(gg);
while (aic_target_frame_elapsed(a_ep)) {
aic_incr_frame_num(a_ep);
/* Update current frame number value once more as it
* changes here.
*/
gg->frame_number = aic_read_frameno(gg);
}
if (a_ep->target_frame != TARGET_FRAME_INITIAL)
aic_ep_start_req(gg, a_ep, a_req, false);
}
return 0;
}
static int aic_ep_queue_request(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
unsigned long flags = 0;
int ret = 0;
spin_lock_irqsave(&gg->lock, flags);
ret = aic_ep_queue_request_nolock(ep, req, gfp_flags);
spin_unlock_irqrestore(&gg->lock, flags);
return ret;
}
static int aic_ep_disable_nolock(struct usb_ep *ep)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
unsigned int index = a_ep->index;
u32 dir_in = a_ep->dir_in;
u32 ctrl_addr;
u32 ctrl;
dev_dbg(gg->dev, "%s(ep %p)\n", __func__, ep);
/* ep0 don't use by app driver */
if (ep == &gg->eps_out[0]->ep) {
dev_err(gg->dev, "%s: called for ep0\n", __func__);
return -EINVAL;
}
ctrl_addr = dir_in ? INEPCFG(index) : OUTEPCFG(index);
ctrl = aic_readl(gg, ctrl_addr);
if (ctrl & EPCTL_EPENA)
aic_ep_stop_xfer(gg, a_ep);
ctrl &= ~EPCTL_EPENA;
ctrl &= ~EPCTL_USBACTEP;
ctrl |= EPCTL_SNAK;
aic_writel(gg, ctrl, ctrl_addr);
dev_dbg(gg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
/* disable endpoint interrupts */
aic_ctrl_epint(gg, a_ep->index, a_ep->dir_in, 0);
/* terminate all requests with shutdown */
aic_kill_ep_reqs(gg, a_ep, -ESHUTDOWN);
/* free fifo */
gg->fifo_map &= ~(1 << a_ep->fifo_index);
gg->tx_fifo_map &= ~(1 << a_ep->index);
a_ep->fifo_index = 0;
a_ep->fifo_size = 0;
return 0;
}
static int aic_ep_disable(struct usb_ep *ep)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
unsigned long flags;
int ret;
spin_lock_irqsave(&gg->lock, flags);
ret = aic_ep_disable_nolock(ep);
spin_unlock_irqrestore(&gg->lock, flags);
return ret;
}
static int aic_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
struct aic_usb_ep *a_ep = our_ep(ep);
struct aic_usb_gadget *gg = a_ep->parent;
unsigned int index = a_ep->index;
unsigned long flags;
u32 dir_in;
u32 mps;
u32 mc;
u32 ep_type;
u32 ctrl_addr;
u32 ctrl;
u32 mask;
int i;
int ret = 0;
dev_dbg(gg->dev,
"%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
__func__, ep->name, desc->bEndpointAddress, desc->bmAttributes,
desc->wMaxPacketSize, desc->bInterval);
/* ep0 don't use by app driver */
if (index == 0) {
dev_err(gg->dev, "%s: called for EP0\n", __func__);
return -EINVAL;
}
dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
if (dir_in != a_ep->dir_in) {
dev_err(gg->dev, "%s: direction mismatch!\n", __func__);
return -EINVAL;
}
spin_lock_irqsave(&gg->lock, flags);
/* (0) read ep ctrl */
ctrl_addr = dir_in ? INEPCFG(index) : OUTEPCFG(index);
ctrl = aic_readl(gg, ctrl_addr);
dev_dbg(gg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
__func__, ctrl, ctrl_addr);
/* (1) max packet */
mps = usb_endpoint_maxp(desc);
mc = usb_endpoint_maxp_mult(desc);
aic_set_ep_maxpacket(gg, a_ep->index, mps, mc, dir_in);
ctrl &= ~(EPCTL_EPTYPE_MASK | EPCTL_MPS_MASK);
ctrl |= EPCTL_MPS(mps);
ctrl |= EPCTL_USBACTEP;
/* (2) endpoint type */
ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
a_ep->isochronous = 0;
a_ep->periodic = 0;
a_ep->halted = 0;
a_ep->interval = desc->bInterval;
switch (ep_type) {
case USB_ENDPOINT_XFER_ISOC:
ctrl |= EPCTL_EPTYPE_ISO;
ctrl |= EPCTL_SETEVENFR;
a_ep->isochronous = 1;
a_ep->interval = 1 << (desc->bInterval - 1);
a_ep->target_frame = TARGET_FRAME_INITIAL;
if (dir_in) {
a_ep->periodic = 1;
mask = aic_readl(gg, INEPINTMSK);
mask |= INEPINTMSK_NAKMSK;
aic_writel(gg, mask, INEPINTMSK);
} else {
mask = aic_readl(gg, OUTEPINTMSK);
mask |= OUTEPINTMSK_OUTTKNEPDISMSK;
aic_writel(gg, mask, OUTEPINTMSK);
}
break;
case USB_ENDPOINT_XFER_BULK:
ctrl |= EPCTL_EPTYPE_BULK;
break;
case USB_ENDPOINT_XFER_INT:
if (dir_in)
a_ep->periodic = 1;
if (gg->gadget.speed == USB_SPEED_HIGH)
a_ep->interval = 1 << (desc->bInterval - 1);
ctrl |= EPCTL_EPTYPE_INTERRUPT;
break;
case USB_ENDPOINT_XFER_CONTROL:
ctrl |= EPCTL_EPTYPE_CONTROL;
break;
}
/* (3) period IN ep alloc fifo */
if (dir_in && ((ep_type == USB_ENDPOINT_XFER_INT) ||
(ep_type == USB_ENDPOINT_XFER_ISOC))) {
u32 fifo_index = 0;
u32 fifo_size = UINT_MAX;
u32 val = 0;
u32 size = 0;
size = a_ep->ep.maxpacket * a_ep->mc;
for (i = 1; i <= gg->params.num_perio_in_ep; i++) {
if (gg->fifo_map & (1 << i))
continue;
val = aic_readl(gg, TXFIFOSIZ(i));
val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4;
if (val < size)
continue;
/* Search for smallest acceptable fifo */
if (val < fifo_size) {
fifo_size = val;
fifo_index = i;
}
}
if (!fifo_index) {
dev_err(gg->dev,
"%s: No suitable fifo found\n", __func__);
ret = -ENOMEM;
goto out;
}
ctrl &= ~(EPCTL_TXFNUM_LIMIT << EPCTL_TXFNUM_SHIFT);
ctrl |= EPCTL_TXFNUM(fifo_index);
gg->fifo_map |= 1 << fifo_index;
a_ep->fifo_index = fifo_index;
a_ep->fifo_size = fifo_size;
} else {
if (dir_in)
gg->tx_fifo_map |= 1 << index;
}
/* (4) for non iso endpoints, set PID to D0 */
if (index && !a_ep->isochronous)
ctrl |= EPCTL_SETD0PID;
/* (5) clear NAK */
if (gg->gadget.speed == USB_SPEED_FULL &&
a_ep->isochronous && dir_in) {
ctrl |= EPCTL_CNAK;
}
/* (6) write-back ep ctrl */
dev_dbg(gg->dev, "%s: write DxEPCTL=0x%08x\n",
__func__, ctrl);
aic_writel(gg, ctrl, ctrl_addr);
dev_dbg(gg->dev, "%s: read DxEPCTL=0x%08x\n",
__func__, aic_readl(gg, ctrl_addr));
/* (7) enable the endpoint interrupt */
aic_ctrl_epint(gg, index, dir_in, 1);
out:
spin_unlock_irqrestore(&gg->lock, flags);
return ret;
}
static struct usb_request *aic_ep_alloc_request(struct usb_ep *ep,
gfp_t flags)
{
struct aic_usb_req *req;
req = kzalloc(sizeof(*req), flags);
if (!req)
return NULL;
INIT_LIST_HEAD(&req->queue);
return &req->req;
}
static void aic_ep_free_request(struct usb_ep *ep,
struct usb_request *req)
{
struct aic_usb_req *aic_req = our_req(req);
kfree(aic_req);
}
static const struct usb_ep_ops aic_usb_ep_ops = {
.enable = aic_ep_enable,
.disable = aic_ep_disable,
.alloc_request = aic_ep_alloc_request,
.free_request = aic_ep_free_request,
.queue = aic_ep_queue_request,
.dequeue = aic_ep_dequeue_request,
.set_halt = aic_ep_sethalt,
};
static int aic_gg_getframe(struct usb_gadget *gadget)
{
return aic_read_frameno(our_gadget(gadget));
}
static int aic_gg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
{
struct aic_usb_gadget *gg = our_gadget(gadget);
if (IS_ERR_OR_NULL(gg->uphy))
return -ENOTSUPP;
return usb_phy_set_power(gg->uphy, mA);
}
static int aic_gg_vbus_session(struct usb_gadget *gadget, int is_active)
{
struct aic_usb_gadget *gg = our_gadget(gadget);
unsigned long flags = 0;
int ret = 0;
if (!gg) {
pr_err("%s: called with no device\n", __func__);
return -ENODEV;
}
dev_dbg(gg->dev, "%s: is_active: %d\n", __func__, is_active);
spin_lock_irqsave(&gg->lock, flags);
if (is_active) {
ret = aic_core_init(gg, false);
if (ret) {
dev_err(gg->dev, "%s: aic_core_init %d\n", __func__, ret);
goto err;
}
if (gg->enabled)
aic_soft_connect(gg);
} else {
aic_soft_disconnect(gg);
aic_core_disconnect(gg);
}
spin_unlock_irqrestore(&gg->lock, flags);
return 0;
err:
spin_unlock_irqrestore(&gg->lock, flags);
return ret;
}
static int aic_gg_pullup(struct usb_gadget *gadget, int is_on)
{
struct aic_usb_gadget *gg = our_gadget(gadget);
unsigned long flags = 0;
int ret = 0;
if (!gg) {
pr_err("%s: called with no device\n", __func__);
return -ENODEV;
}
dev_dbg(gg->dev, "%s: is_on: %d\n", __func__, is_on);
spin_lock_irqsave(&gg->lock, flags);
if (is_on) {
gg->enabled = 1;
ret = aic_core_init(gg, false);
if (ret) {
dev_err(gg->dev, "%s: aic_core_init %d\n", __func__, ret);
goto err;
}
aic_soft_connect(gg);
} else {
aic_soft_disconnect(gg);
aic_core_disconnect(gg);
gg->enabled = 0;
}
gg->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock_irqrestore(&gg->lock, flags);
return 0;
err:
spin_unlock_irqrestore(&gg->lock, flags);
return ret;
}
static int aic_gg_udc_stop(struct usb_gadget *gadget)
{
struct aic_usb_gadget *gg = our_gadget(gadget);
unsigned long flags = 0;
int i = 0;
int ret = 0;
if (!gg) {
pr_err("%s: called with no device\n", __func__);
return -ENODEV;
}
/* all endpoints should be shutdown */
for (i = 1; i < gg->params.num_ep; i++) {
if (gg->eps_in[i])
aic_ep_disable(&gg->eps_in[i]->ep);
if (gg->eps_out[i])
aic_ep_disable(&gg->eps_out[i]->ep);
}
spin_lock_irqsave(&gg->lock, flags);
gg->driver = NULL;
gg->gadget.speed = USB_SPEED_UNKNOWN;
gg->enabled = 0;
spin_unlock_irqrestore(&gg->lock, flags);
#ifdef CONFIG_USB_OTG
if (!IS_ERR_OR_NULL(gg->uphy))
otg_set_peripheral(gg->uphy->otg, NULL);
#endif
ret = aic_low_hw_disable(gg);
if (ret) {
dev_err(gg->dev, "%s: aic_low_hw_disable %d\n", __func__, ret);
return ret;
}
return 0;
}
static int aic_gg_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct aic_usb_gadget *gg = our_gadget(gadget);
unsigned long flags = 0;
int ret = 0;
if (!gg) {
pr_err("%s: called with no device\n", __func__);
return -ENODEV;
}
if (!driver) {
dev_err(gg->dev, "%s: no driver\n", __func__);
return -EINVAL;
}
if (driver->max_speed < USB_SPEED_FULL)
dev_err(gg->dev, "%s: bad speed\n", __func__);
if (!driver->setup) {
dev_err(gg->dev, "%s: missing entry points\n", __func__);
return -EINVAL;
}
WARN_ON(gg->driver);
driver->driver.bus = NULL;
gg->driver = driver;
gg->gadget.dev.of_node = gg->dev->of_node;
gg->gadget.speed = USB_SPEED_UNKNOWN;
ret = aic_low_hw_enable(gg);
if (ret) {
dev_err(gg->dev, "%s: aic_low_hw_enable %d\n", __func__, ret);
goto err;
}
#ifdef CONFIG_USB_OTG
if (!IS_ERR_OR_NULL(gg->uphy))
otg_set_peripheral(gg->uphy->otg, &gg->gadget);
#endif
spin_lock_irqsave(&gg->lock, flags);
ret = aic_core_init(gg, false);
if (ret) {
dev_err(gg->dev, "%s: aic_core_init %d\n", __func__, ret);
goto err;
}
gg->enabled = 0;
spin_unlock_irqrestore(&gg->lock, flags);
dev_info(gg->dev, " bound driver %s\n", driver->driver.name);
return 0;
err:
gg->driver = NULL;
return ret;
}
static struct usb_ep *aic_gg_match_ep(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp)
{
struct aic_usb_gadget *gg = our_gadget(gadget);
int ep_num = usb_endpoint_num(desc);
struct usb_ep *ep;
if ((ep_num >= gg->params.num_ep) || (ep_num == 0))
return NULL;
if (usb_endpoint_dir_in(desc))
ep = &gg->eps_in[ep_num]->ep;
else
ep = &gg->eps_out[ep_num]->ep;
if (ep->claimed)
return NULL;
if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp))
return ep;
return NULL;
}
static const struct usb_gadget_ops aic_usb_gadget_ops = {
.get_frame = aic_gg_getframe,
.udc_start = aic_gg_udc_start,
.udc_stop = aic_gg_udc_stop,
.pullup = aic_gg_pullup,
.vbus_session = aic_gg_vbus_session,
.vbus_draw = aic_gg_vbus_draw,
.match_ep = aic_gg_match_ep,
};
static void aic_init_ep(struct aic_usb_gadget *gg,
struct aic_usb_ep *ep,
int epnum,
bool dir_in)
{
char *dir;
ep->dir_in = dir_in;
ep->index = epnum;
ep->parent = gg;
if (epnum == 0)
dir = "";
else if (dir_in)
dir = "in";
else
dir = "out";
snprintf(ep->name, sizeof(ep->name), "ep%d%s", epnum, dir);
ep->ep.name = ep->name;
INIT_LIST_HEAD(&ep->queue);
INIT_LIST_HEAD(&ep->ep.ep_list);
/* add ep1 ~ epN to the list of endpoints known by the gadget driver */
if (epnum)
list_add_tail(&ep->ep.ep_list, &gg->gadget.ep_list);
if (gg->params.speed == AIC_SPEED_PARAM_LOW)
usb_ep_set_maxpacket_limit(&ep->ep, 8);
else
usb_ep_set_maxpacket_limit(&ep->ep,
epnum ? 1024 : EP0_MPS_LIMIT);
ep->ep.ops = &aic_usb_ep_ops;
if (epnum == 0) {
ep->ep.caps.type_control = true;
} else {
if (gg->params.speed != AIC_SPEED_PARAM_LOW) {
ep->ep.caps.type_iso = true;
ep->ep.caps.type_bulk = true;
}
ep->ep.caps.type_int = true;
}
if (dir_in)
ep->ep.caps.dir_in = true;
else
ep->ep.caps.dir_out = true;
}
static int aic_gadget_core_init(struct aic_usb_gadget *gg)
{
struct device *dev = gg->dev;
u32 cfg = gg->params.ep_dirs;
u32 ep_type = 0;
int i = 0;
spin_lock_init(&gg->lock);
/* alloc ep0 */
gg->eps_in[0] = devm_kzalloc(gg->dev,
sizeof(struct aic_usb_ep),
GFP_KERNEL);
if (!gg->eps_in[0])
return -ENOMEM;
gg->eps_out[0] = gg->eps_in[0];
/* alloc ep0 buffer */
gg->ctrl_buff = devm_kzalloc(gg->dev,
ALIGN(CTRL_BUFF_SIZE, L1_CACHE_BYTES),
GFP_KERNEL);
if (!gg->ctrl_buff)
return -ENOMEM;
gg->ep0_buff = devm_kzalloc(gg->dev,
ALIGN(CTRL_BUFF_SIZE, L1_CACHE_BYTES),
GFP_KERNEL);
if (!gg->ep0_buff)
return -ENOMEM;
/* alloc ep0 request */
gg->ctrl_req = aic_ep_alloc_request(&gg->eps_out[0]->ep,
GFP_KERNEL);
if (!gg->ctrl_req) {
dev_err(dev, "failed to allocate ctrl req\n");
return -ENOMEM;
}
/* alloc other ep */
for (i = 1, cfg >>= 2; i < gg->params.num_ep; i++, cfg >>= 2) {
ep_type = cfg & 3;
/* Direction in or both */
if (!(ep_type & 2)) {
gg->eps_in[i] = devm_kzalloc(gg->dev,
sizeof(struct aic_usb_ep),
GFP_KERNEL);
if (!gg->eps_in[i])
return -ENOMEM;
}
/* Direction out or both */
if (!(ep_type & 1)) {
gg->eps_out[i] = devm_kzalloc(gg->dev,
sizeof(struct aic_usb_ep),
GFP_KERNEL);
if (!gg->eps_out[i])
return -ENOMEM;
}
}
/* init eps */
INIT_LIST_HEAD(&gg->gadget.ep_list);
for (i = 0; i < gg->params.num_ep; i++) {
if (gg->eps_in[i])
aic_init_ep(gg, gg->eps_in[i], i, 1);
if (gg->eps_out[i])
aic_init_ep(gg, gg->eps_out[i], i, 0);
}
/* gadget member */
gg->gadget.max_speed = USB_SPEED_HIGH;
gg->gadget.ops = &aic_usb_gadget_ops;
gg->gadget.name = dev_name(dev);
gg->gadget.ep0 = &gg->eps_out[0]->ep;
return 0;
}
static int aic_param_init(struct aic_usb_gadget *gg)
{
gg->params.num_ep = EPS_NUM;
gg->params.num_perio_in_ep = PERIOD_IN_EP_NUM;
gg->params.total_fifo_size = TOTAL_FIFO_SIZE;
gg->params.rx_fifo_size = RX_FIFO_SIZE;
gg->params.np_tx_fifo_size = NP_TX_FIFO_SIZE;
gg->params.p_tx_fifo_size[1] = PERIOD_TX_FIFO1_SIZE;
gg->params.p_tx_fifo_size[2] = PERIOD_TX_FIFO2_SIZE;
gg->params.ep_dirs = EP_DIRS;
#ifdef CONFIG_DEBUG_ON_FPGA_BOARD_ARTINCHIP
gg->params.phy_type = AIC_PHY_TYPE_PARAM_ULPI;
#else
gg->params.phy_type = AIC_PHY_TYPE_PARAM_UTMI;
#endif
gg->params.phy_ulpi_ddr = 0;
gg->params.speed = AIC_SPEED_PARAM_HIGH;
return 0;
}
static int aic_gadget_init(struct aic_usb_gadget *gg)
{
int ret = 0;
ret = aic_param_init(gg);
if (ret) {
dev_err(gg->dev, "call aic_param_init fail: %d\n", ret);
return ret;
}
ret = aic_gadget_core_init(gg);
if (ret) {
dev_err(gg->dev, "call aic_gadget_core_init fail: %d\n", ret);
return ret;
}
return ret;
}
static int aic_udc_remove(struct platform_device *dev)
{
struct aic_usb_gadget *gg = platform_get_drvdata(dev);
#ifdef CONFIG_USB_OTG
if (!IS_ERR_OR_NULL(gg->uphy))
usb_put_phy(gg->uphy);
#endif
aic_udc_debugfs_exit(gg);
usb_del_gadget_udc(&gg->gadget);
aic_ep_free_request(&gg->eps_out[0]->ep, gg->ctrl_req);
aic_low_hw_disable(gg);
reset_control_assert(gg->reset);
reset_control_assert(gg->reset_ecc);
return 0;
}
static void aic_udc_shutdown(struct platform_device *dev)
{
struct aic_usb_gadget *gg = platform_get_drvdata(dev);
disable_irq(gg->irq);
}
static int aic_udc_probe(struct platform_device *dev)
{
struct aic_usb_gadget *gg = NULL;
struct resource *res = NULL;
int i, err;
int ret = 0;
if (of_property_read_bool(dev->dev.of_node, "aic,only-uboot-use")) {
dev_info(&dev->dev, "aic-udc only work in uboot.\n");
return -EPERM;
}
gg = devm_kzalloc(&dev->dev, sizeof(*gg), GFP_KERNEL);
if (!gg)
return -ENOMEM;
gg->dev = &dev->dev;
if (!dev->dev.dma_mask)
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
ret = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", ret);
return ret;
}
/* register */
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
gg->regs = devm_ioremap_resource(&dev->dev, res);
if (IS_ERR(gg->regs)) {
dev_err(&dev->dev, "ioremap reg fail!\n");
return PTR_ERR(gg->regs);
}
dev_dbg(&dev->dev, "mapped physical addr %08lx to virtual addr %p\n",
(unsigned long)res->start, gg->regs);
/* reset */
gg->reset = devm_reset_control_get_optional(gg->dev, "aicudc");
if (IS_ERR(gg->reset)) {
ret = PTR_ERR(gg->reset);
dev_err(gg->dev, "error getting reset control %d\n", ret);
return ret;
}
gg->reset_ecc = devm_reset_control_get_optional_shared(gg->dev,
"aicudc-ecc");
if (IS_ERR(gg->reset_ecc)) {
ret = PTR_ERR(gg->reset_ecc);
dev_err(gg->dev, "error getting reset control for ecc %d\n",
ret);
return ret;
}
/* regulator */
aic_gg_get_res_cfg(dev->dev.of_node, &gg->params.usb_res_cfg, "aic,usbd-ext-resistance");
/* clock */
for (i = 0; i < USB_MAX_CLKS_RSTS; i++) {
gg->clks[i] = of_clk_get(gg->dev->of_node, i);
if (IS_ERR(gg->clks[i])) {
dev_err(gg->dev, "cannot get clock %d\n", i);
return PTR_ERR(gg->clks[i]);
err = PTR_ERR(gg->clks[i]);
if (err == -EPROBE_DEFER)
return err;
gg->clks[i] = NULL;
break;
}
}
/* phy */
gg->phy = devm_phy_get(gg->dev, "usb2-phy");
if (IS_ERR(gg->phy)) {
ret = PTR_ERR(gg->phy);
switch (ret) {
case -ENODEV:
case -ENOSYS:
gg->phy = NULL;
break;
case -EPROBE_DEFER:
return ret;
default:
dev_err(gg->dev, "error getting phy %d\n", ret);
return ret;
}
}
#ifdef CONFIG_USB_OTG
if (!gg->phy &&
(of_property_read_bool(dev->dev.of_node, "aic,otg-support"))) {
gg->uphy = devm_usb_get_phy(gg->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(gg->uphy)) {
ret = PTR_ERR(gg->uphy);
switch (ret) {
case -ENODEV:
case -ENXIO:
gg->uphy = NULL;
break;
case -EPROBE_DEFER:
return ret;
default:
dev_err(gg->dev, "error getting usb phy %d\n",
ret);
return ret;
}
}
}
#endif
/* gadget init */
ret = aic_gadget_init(gg);
if (ret) {
dev_err(&dev->dev, "udc init fail: %d\n", ret);
return ret;
}
/* interrupt */
res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&dev->dev, "No IRQ resource found!\n");
return -ENODEV;
}
gg->irq = res->start;
ret = devm_request_irq(gg->dev, gg->irq,
aic_udc_irq, IRQF_SHARED,
dev_name(gg->dev), gg);
if (ret) {
dev_err(&dev->dev, "request irq%d fail: %d\n", gg->irq, ret);
return ret;
}
dev_dbg(gg->dev, "registering interrupt handler for irq%d\n",
gg->irq);
/* udc add */
ret = usb_add_gadget_udc(gg->dev, &gg->gadget);
if (ret) {
dev_err(&dev->dev, "udc add fail: %d\n", ret);
return ret;
}
/* debugfs */
aic_udc_debugfs_init(gg);
platform_set_drvdata(dev, gg);
return 0;
}
const struct of_device_id aic_udc_match_table[] = {
{ .compatible = "artinchip,aic-udc-v1.0",},
{},
};
static int __maybe_unused aic_udc_suspend(struct device *dev)
{
struct aic_usb_gadget *gg = dev_get_drvdata(dev);
aic_low_hw_disable(gg);
return 0;
}
static int __maybe_unused aic_udc_resume(struct device *dev)
{
struct aic_usb_gadget *gg = dev_get_drvdata(dev);
aic_low_hw_enable(gg);
return 0;
}
static const struct dev_pm_ops aic_udc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(aic_udc_suspend, aic_udc_resume)
};
static struct platform_driver aic_udc_driver = {
.driver = {
.name = "aic_udc",
.of_match_table = aic_udc_match_table,
.pm = &aic_udc_pm_ops,
},
.probe = aic_udc_probe,
.remove = aic_udc_remove,
.shutdown = aic_udc_shutdown,
};
module_platform_driver(aic_udc_driver);
MODULE_DESCRIPTION("USB Device Controller driver for aic");